达梦数据库读写分离集群部署实战:数据守护与故障切换全记录

发布时间:2026/10/19 6:28:43
达梦数据库读写分离集群部署实战:数据守护与故障切换全记录
上个月我们这边出了一次数据库CPU告警持续了快二十分钟。值班同学把慢日志拉出来一看全是同一个报表模块的查询一条条都是全表扫描级别的家伙。索引该加的也加了SQL该改的也改了问题根源其实很清楚读请求量太大单库已经到了极限。于是我们把目光放到了达梦数据库的读写分离集群上。这篇文章就是我这次部署的完整记录包含主备库搭建、数据守护配置、读写分离路由、故障切换验证以及我在实际过程中踩过的几个坑。如果你正打算在一个读多写少的业务场景里上达梦读写分离这篇可以直接照着操作。1. 动手之前先把方案选明白达梦的高可用玩法不止一种1.1 达梦集群方案的差异数据守护、DSC与读写分离集群很多人一听“读写分离集群”第一反应就是“不就是一主一备嘛”。这句话对了一半但真到了达梦这里至少有三条路可以走选错方向后面会很难受。达梦数据库常见的集群方案大致分成三类方案核心思路适合场景读写能力数据守护主库实时同步到备库故障时切换高可用、容灾备库平时不对外提供读写大多处于只读状态DSC共享集群多个实例共享一套数据文件类似通用数据库的RAC高并发、高可用、负载均衡所有实例都能提供完整读写能力读写分离集群主库提供写备库提供读应用侧路由读多写少、报表类业务写走主库读可分担到备库我这次要部署的就是第三类。读写分离集群一般也基于数据守护同步但比单纯的数据守护多了一层“读流量调度”。也就是说备库平时不能只躺在那等切换它得实打实地把只读SQL扛起来这才是“读写分离”的核心价值。1.2 读写分离集群的运作逻辑说人话就是主库负责INSERT、UPDATE、DELETE这些写操作备库负责SELECT这种只读操作。中间靠数据守护机制把主库的Redo日志实时传递到备库并回放保证主备数据基本一致。外部应用连接的时候需要有一个路由层来决定“这条SQL发给谁”。达梦的官方方案里JDBC驱动和中间件都提供了读写分离能力。实际部署时你可以在两个层面做选择应用层路由在代码里给读操作和写操作配置不同的数据源最简单、最直观。JDBC驱动层路由利用达梦JDBC驱动自带的主备识别和SQL类型判断能力自动把只读语句转发到备库应用代码改动最小。我在这次部署里验证了两种方式都能跑通后面第5章会详细说怎么配。1.3 什么样的业务适合上这套方案不是所有系统都适合读写分离。我个人的判断标准是这么几条读量远大于写量比如报表、查询、大屏展示类系统读写比至少在5比1以上。对主备同步延迟有一定容忍度。虽然达梦的Redo同步很快但备库数据始终是“接近实时”而不是“完全同步”要求强一致读的业务要慎重。现有单库CPU/IO已经出现瓶颈而且通过加索引、改SQL已经压不下来了。如果你满足这三条读写分离集群就是值得考虑的方案。下面的所有部署步骤都是基于这个判断展开的。2. 准备阶段决定成败版本、环境、初始化参数一个都不能乱2.1 环境规划与目录规范我建议在动手前先把节点规划表列出来别等到配配置的时候才发现IP写错。我的环境是这样的角色实例名IP实例端口MAL端口数据目录归档目录主库DMPRI192.168.10.1152365271/dm/data/DMPRI/dm/arch/DMPRI备库DMSTB192.168.10.1252365271/dm/data/DMSTB/dm/arch/DMSTB这里有个容易踩的坑MAL端口和实例端口不能冲突主备实例端口可以相同因为不在同一台机器上但MAL网络必须互通而且防火墙要放行5236、5271、5270这几个端口。我第一遍部署时就是忘了放行5271结果守护进程之间一直互相不可见折腾了半个多小时。目录方面我的习惯是数据目录和归档目录分开放在不同磁盘挂载点避免归档写满后把数据盘也拖死。如果条件允许Redo日志也单独放一个目录性能会更好。2.2 达梦安装与实例初始化达梦的软件安装本身不复杂官方安装包解压后执行DMInstall.bin就行。但有几个前置操作必须做# 创建数据库管理用户 useradd -m dmdba -s /bin/bash # 调整文件句柄限制 vim /etc/security/limits.conf # 增加以下内容 dmdba soft nofile 65536 dmdba hard nofile 65536 # 创建目录并授权 mkdir -p /dm/data /dm/arch /dm/backup /dm/bin chown -R dmdba:dmdba /dm # 设置环境变量 su - dmdba vim ~/.bash_profile # 增加 export DM_HOME/dm export PATH$DM_HOME/bin:$PATH安装完成后用dminit初始化实例。这里有个关键点主库和备库的初始化参数必须完全一致尤其是页大小、字符集、大小写敏感这三项。如果主库用32K页、备库用16K页后面同步一定会出问题。su - dmdba /ddm/bin/dminit PATH/dm/data/DMPRI INSTANCE_NAMEDMPRI PORT_NUM5236 \ PAGE_SIZE32 LOG_SIZE2048 CHARSET0 CASE_SENSITIVEYCHARSET0表示UTF8字符集CASE_SENSITIVEY表示大小写敏感。这两个参数一旦初始化就不能改了所以开工前务必确认清楚。2.3 基础参数调整忽略这一节后面必然后悔实例起来之后先别急着做集群把几个基础参数调好。我用的是达梦的sp_set_para_value动态调整部分静态参数修改后需要重启实例。-- 启用MAL系统读写分离集群的必要条件 SP_SET_PARA_VALUE(2, MAL_INI, 1); -- 启用归档数据同步依赖归档日志 SP_SET_PARA_VALUE(2, ARCH_INI, 1); -- 配置实例为实时归档模式 SP_SET_PARA_VALUE(2, ARCH_MODE, 1);调完参数后登录数据库检查确认SELECT NAME, VALUE FROM V$PARAMETER WHERE NAME IN (MAL_INI, ARCH_INI);我习惯在每台机器上都执行一遍确保主备一致。如果发现某个参数不一致及时处理再继续往下不要带着隐患做集群。3. 主备库建立备份、还原、同步链路三步走3.1 主库开启归档并做全量备份整个读写分离集群的基础就是一套完整的主备数据副本。我的做法是在主库开启归档后做一次全量备份然后用这份备份到备库上做还原。主库执行以下操作-- 将数据库切换到MOUNT状态 ALTER DATABASE MOUNT; -- 开启归档模式 ALTER DATABASE ARCHIVELOG; -- 回到OPEN状态 ALTER DATABASE OPEN; -- 执行全量备份 BACKUP DATABASE FULL BACKUPSET /dm/backup/init_full_bk;这里建议备份完成后检查一下备份集是否完整ls -lh /dm/backup/init_full_bk备份集里应该有三个关键文件备份描述文件、数据文件镜像、归档日志文件。如果缺少任何一个备库还原都会出问题。3.2 备库初始化与数据还原备库这边我先把空实例初始化出来再用dmrman工具做还原。注意备库的dminit参数必须和主库一模一样否则还原后的库无法启动。# 初始化备库实例 /ddm/bin/dminit PATH/dm/data/DMSTB INSTANCE_NAMEDMSTB PORT_NUM5236 \ PAGE_SIZE32 LOG_SIZE2048 CHARSET0 CASE_SENSITIVEY # 使用dmrman还原主库备份 cd /dm/bin ./dmrman CTLSTMTRESTORE DATABASE /dm/data/DMSTB/dm.ini FROM BACKUPSET /dm/backup/init_full_bk # 恢复数据库并更新DB_MAGIC ./dmrman CTLSTMTRECOVER DATABASE /dm/data/DMSTB/dm.ini UPDATE DB_MAGIC还原和恢复完成后启动备库实例然后执行以下SQL把备库切换为STANDBY模式ALTER DATABASE STANDBY;这一步经常有人漏掉如果不切模式备库会一直处于NORMAL模式守护进程根本没法把它纳入集群。3.3 主备一致性校验切换完模式后我习惯在主备库各查一下表数量和数据条数确认基线数据一致。SELECT COUNT(*) FROM USER_TABLES; SELECT COUNT(*) FROM DBA_TABLES;再对比几张大表的行数比如主库跑一条备库跑同一条。如果数据一致说明备份还原链路是通的可以继续做数据守护配置。4. 数据守护配置读写分离集群的神经中枢4.1 四个配置文件逐个说明数据守护是达梦读写分离集群的核心。它由三部分构成主库、备库、守护进程外加一个确认监控器。整套机制的配置分布在几个文件里我把关键字段说明一下。首先是dmmal.ini负责MAL通信链路主备库内容基本一样MAL_CHECK_INTERVAL 5 MAL_CONN_FAIL_INTERVAL 10 MAL_BUF_SIZE 100 MAL_SYS_IO_WAIT_TIME 20 MAL_COMPRESS_LEVEL 0 [MAL_INST1] MAL_INST_NAME DMPRI MAL_HOST 192.168.10.11 MAL_PORT 5271 MAL_INST_HOST 192.168.10.11 MAL_INST_PORT 5236 MAL_INST_DR_PORT 5270 [MAL_INST2] MAL_INST_NAME DMSTB MAL_HOST 192.168.10.12 MAL_PORT 5271 MAL_INST_HOST 192.168.10.12 MAL_INST_PORT 5236 MAL_INST_DR_PORT 5270其中MAL_INST_DR_PORT是数据守护专用的通信端口两边要一致且不能被占用。然后是dmarch.ini配置归档参数[ARCHIVE_LOCAL1] ARCH_TYPE LOCAL ARCH_DEST /dm/arch/DMPRI ARCH_FILE_SIZE 2048 ARCH_SPACE_LIMIT 10240备库的ARCH_DEST换成自己的目录。ARCH_SPACE_LIMIT10240表示归档空间上限10GB这个值一定要设否则归档一直堆积会把磁盘塞满。接下来是dmwatcher.ini守护进程配置文件主备库各一份主库的INST_INC为0备库为1[GRP1] DW_TYPE GLOBAL DW_MODE AUTO DW_ERROR_TIME 15 INST_RECOVER_TIME 60 INST_OGUID 453331 INST_INC 0 INST_STARTUP_CMD /dm/bin/dmserverDW_MODEAUTO表示自动切换模式INST_OGUID是集群唯一标识两边的值必须一致。INST_STARTUP_CMD是守护进程拉起实例时要执行的启动命令注意路径别写错。最后是dmmonitor.ini监控器配置文件。我这边用的是确认监控器只在其中一台机器上部署[GRP1] MON_INST_OGUID 453331 MON_DW_CONFIRM 1 MON_LOG_PATH /dm/log MON_LOG_INTERVAL 60 MON_LOG_FILE_SIZE 1024 MON_LOG_SPACE_LIMIT 10240 AUTO_RESTART 1MON_DW_CONFIRM1表示这台监控器是确认监控器故障切换时拥有最终决定权。4.2 设置OGUID与主备模式切换配置文件都准备好之后需要先设置OGUID并再次确认模式。主备库分别登录执行-- 设置守护集群OGUID SP_SET_OGUID(453331); -- 再次确保模式正确主库为PRIMARY备库为STANDBY SELECT MODE$ FROM V$INSTANCE;如果备库模式不是STANDBY执行ALTER DATABASE STANDBY;这一步做完主备库上的实例级配置就基本齐了。接下来需要初始化守护进程和监控器让它们互相发现对方。4.3 守护进程与监控器启动顺序启动顺序是我这次踩坑最多的地方。正确的顺序是先启动主库实例再启动备库实例启动主库的守护进程启动备库的守护进程最后启动确认监控器# 主备库分别启动实例然后用dmdba用户执行守护进程 su - dmdba cd /dm/bin ./DmWatcher /dm/data/DMPRI/dmwatcher.ini # 备库同样 su - dmdba cd /dm/bin ./DmWatcher /dm/data/DMSTB/dmwatcher.ini # 监控器机器上启动监控器 cd /dm/bin ./dmmonitor /dm/data/dmmonitor.ini监控器启动后进入交互界面输入show database info查看集群状态。正常情况下主库显示PRIMARY备库显示STANDBY并且两边的REDO_FLOW数值在不停增长。这里有个容易卡住的地方如果守护进程启动后一直显示WF状态说明实例和守护进程之间没有建立连接。优先检查dmwatcher.ini里的INST_STARTUP_CMD路径是否正确再看防火墙是否放行了5271端口。5. 让查询真正跑到备库路由配置与验证方法5.1 JDBC接入方式会话级路由数据守护集群就绪后最关键的一步是让应用流量按SQL类型做路由。如果你的应用用的是达梦官方JDBC驱动可以在连接URL层面配置读写分离参数。我这里演示一下整体思路jdbc:dm://192.168.10.11:5236?readSeparatetruestandbyHosts192.168.10.12:5236readOnlytrue说明一下readSeparatetrue是开启读写分离的开关standbyHosts是备库地址列表。不同版本达梦JDBC驱动对这个参数的支持略有差异具体参数名要以你手里版本的官方JDBC手册为准。我上面的写法是原理性示例不是每个版本都一模一样。如果你不想直接依赖驱动层路由更稳妥的做法是配两个数据源。主数据源指向主库备数据源指向备库然后在代码层面对只读SQL走备数据源。这种方式最可控排错也容易。5.2 中间件路由对应用透明的方案如果应用代码不方便大改可以考虑在中间件层做读写分离。选择标准就是看中间件是否支持多数据源路由和读写分离策略。我这边用的是应用服务器自带的数据源配置把主库和备库分别注册为不同的数据源然后通过数据源路由拦截器将只读方法强制路由到备库。中间件的具体配置细节因产品而异但核心逻辑都一样根据SQL前缀判断读操作还是写操作读的走备库写的走主库。无论用哪种路由方式都必须注意一点事务里一旦出现了写操作整个事务就应该固定在主库执行。否则可能出现先写后读时备库还没来得及同步读到的是旧数据。5.3 路由生效验证配置完成后怎么确认读写分离真的生效了我在主备库分别开了一个监视窗口用几类典型SQL做测试执行一条简单的SELECT COUNT(*) FROM BIG_TABLE看会话落在哪边。执行一条INSERT操作看主库会话数增长。反复执行多条只读SQL观察备库会话数是否同步上涨。通过查询会话视图可以对比主备库的连接来源SELECT CLNT_IP, COUNT(*) FROM V$SESSIONS GROUP BY CLNT_IP;正常情况下只读连接的来源IP会集中出现在备库。如果备库始终没有新会话说明路由配置没有生效优先排查JDBC驱动的参数是否被正确解析以及中间件的路由规则是否覆盖了对应方法。6. 切换演练和日常巡检部署完不等于能稳定跑6.1 主库故障自动切换验证读写分离集群比单库的底气就在于故障切换。我在部署完成后专门做了一次模拟故障演练目的是验证备库能不能在无人干预的情况下自动顶上。演练操作很简单在主库机器上直接kill掉dmserver进程然后观察确认监控器的状态变化。kill -9 $(pgrep dmserver)正常情况下监控器会在DW_ERROR_TIME指定的15秒内检测到主库无响应然后自动执行切换把原备库提升为主库。我在日志里看到的关键状态是SWITCHOVER SUCCESS原备库从STANDBY变成了PRIMARY并开始对外提供完整读写服务。需要特别提醒的是故障切换完成后原主库不会自己回来当主库。把原主库修好后重新启动它会以STANDBY模式自动加入集群继续作为备库角色工作。如果你想让它重新当主库需要手动做一次主备切换我一般是在确认监控器里执行switchover命令。6.2 备库延迟与归档日志的日常巡检读写分离集群跑起来之后我最担心的是备库同步延迟。如果延迟越来越大说明备库回放速度跟不上主库写入速度这时候就算把读流量导过去应用查到的数据也一定是过期的。我目前的巡检习惯是每天早上一遍、下午一遍登录确认监控器执行show database info检查两边的REDO_FLOW是否持续递增。看归档目录占用如果超过容量的70%就要处理。在主库执行更新再连备库查同一条数据观察数据更新时间差。归档日志这块尤其要注意。ARCH_SPACE_LIMIT虽然设置了上限但归档满了之后主库会停止写入严重后果是整库不可写。我后来专门写了一个定时任务定期清理已被备库应用完成的归档文件避免磁盘被撑爆。6.3 我遇到过的几个典型问题最后把我这次部署排错的重点问题列一下给后来人提个醒问题现象可能原因处理方式守护进程一直显示WF状态防火墙未放行MAL端口或INST_STARTUP_CMD路径错误放行端口检查路径后重启守护进程备库数据一直不更新备库模式还是NORMAL或归档目录不可写执行ALTER DATABASE STANDBY检查目录权限主备切换后应用连接异常应用连接串写死了主库地址没有动态感知主备变化改用JDBC驱动读写分离方式或引入服务代理层归档目录磁盘满ARCH_SPACE_LIMIT未设置设置上限并补充归档清理脚本这四类问题里最容易出问题的其实是第二项。我遇到过一次备库模式被误改成NORMAL结果集群同步静默中断主库还一直往里写备库永远停在那一个时间点。后来我把备库模式检查写进了部署验证脚本里每次巡检自动跑一遍再没出过类似问题。最后分享一个小经验整套集群部署完成后一定要把“启动顺序”这件事写成文档交给运维同学。达梦数据守护集群的启动顺序非常严格一旦监控器先于守护进程启动就会误判主库故障引发不必要的切换。我吃过这个亏后面在服务启动脚本里加了依赖关系才算彻底安心。