达梦数据库读写分离集群搭建实战:主备同步与故障切换 一个生产库单机跑了两年的达梦读多写少平时CPU也就40%一到月初报表和业务查询撞在一起直接冲到90%以上慢SQL满屏飘。团队里讨论了一轮第一反应都是上读写分离集群。可真的上了之后才发现关键不是怎么把备库搭起来而是怎么让主备之间既保持同步稳定又能扛住读流量还要在故障切换时让业务尽量无感。这篇文章把我从选型、配置到测试验证踩过的坑拆开讲一遍内容偏实操适合已经会用达梦、但还没在生产环境搭过读写分离集群的同行参考。1. 先想明白读写分离集群解决什么问题哪种路线才适合你1.1 读多写少场景下的瓶颈到底出在哪读多写少的库单实例上读写混跑最常见的问题不只是CPU高而是缓存池和锁竞争互相拖累。一次大的报表查询扫过上千万行把数据页全部冲进缓存随后写事务需要的热点页被挤出去磁盘IO立刻开始飙升查询和写事务还要共享同一份行锁、锁表资源查询一阻塞写事务的响应时间也会跟着变差。这种场景上读写分离集群本质目的是把读请求从主实例上剥离出去让主库专心处理写事务备库分担查询压力。但这里要有一个预期管理备库读到的数据一定存在延迟。哪怕网络和日志回放再快也做不到物理意义上的零延迟。业务如果要求读己之写立即一致就必须在应用层做标记比如写完后走一次主库或者通过本地缓存保证同一条数据在短时间内不被分流到备库。这是方案设计阶段就要跟业务讲清楚的否则上线后一定会被当成故障来投诉。另外一个容易忽略的代价是运维复杂度。主备切换之后连接池里的连接缓存、只读数据源指向、监控告警策略全都要跟着变。一个集群如果没人认真维护切一次主往往比写坏一次数据还要麻烦。1.2 达梦环境下的两种落地路线对比从我实际见过的情况来看达梦读写分离有两种常见落地方式。第一种是数据守护主备加应用层分流。先搭一套实时主备备库作为只读节点业务代码里自己区分读接口和写接口读请求连接备库地址写请求连接主库地址。好处是改动直观不依赖驱动特性出了问题好排查坏处是数据库本身不感知路由业务代码要维护两套连接而且很容易出现新来的同事只改了一个查询没走备库数据源的情况。第二种是达梦集群自带的读写分离能力。主库和备库之间通过守护机制同步连接层根据SQL类型自动把读请求分发到备库写请求留在主库。对应用来说更像是在连一个逻辑数据库改造量比第一种小。但对DBA的要求更高因为你要理解集群内部的角色判定、连接分发规则和异常时会不会把流量打回主库。这两条路线没有绝对好坏我大部分场景推荐第一种因为只要能控制代码路由排查链路就短也容易理解。如果团队里DBA维护能力很强又想少动应用代码再考虑第二种。对比项数据守护主备 应用层分流集群自带读写分离能力读路由应用代码或中间件控制连接层自动分发写路由主库连接主库连接故障切换依赖守护监视器依赖集群管理应用改造成本高一些低一些适合情况已有主备、团队能改代码新系统、DBA能力强1.3 哪些场景不建议硬上如果单库本身的CPU长期低于50%核心业务的响应时间也满足要求完全没必要上读写分离集群。读写分离的收益来自真实的读压力而不是来自主备是双机这个形式感。写多读少的系统也不适合。备库日志回放本身会消耗CPU和磁盘IO如果主库大部分时间都在写备库不停回放并没有多少查询流量能被分担集群反而成了累赘。还有大事务特别多的场景也要谨慎。备库在回放一个大事务时读请求可能被卡住延迟会明显放大。强一致要求极高的业务同样不适合。很多金融类系统里不允许读到旧数据那读写分离从本质上就不成立。这类场景不如把预算花在资源扩容或优化SQL上。2. 动手之前环境规划和初始化细节比配置本身更影响成败2.1 主机、磁盘与网络基线达梦读写分离集群对硬件的要求并不复杂但主备两台机器的性能差距不要太大。否则平时看起来没问题一旦发生故障切换新主库扛不住原来主库的流量整个业务反而先崩掉。项目建议主备实例配置尽量一致至少CPU核心数一致备库明显低于主库会埋雷内存按数据量估算缓存池通常给到物理内存的60%左右磁盘数据盘、日志盘、归档盘分开不要共用一块物理盘网络主备在同一机房RTT小于1ms不要跨区域部署时钟所有节点部署NTP同步时间差控制在秒级以内我特别想强调网络问题。主备同步依赖网络传输归档日志网络抖动会导致守护进程误判故障并触发切换。以前遇到过一种情况主备两个节点防火墙策略没放通某个监控端口结果监视器收不到心跳不停报主库故障其实主库完全正常。所以网络规划不只是看带宽还要看防火墙、路由是否存在单向丢包以及交换机上有没有丢包报警。2.2 dminit初始化和dm.ini中不能改的参数初始化这一步最容易让新手踩坑。达梦数据库的页大小、字符集这些参数在初始化之后基本改不了主备库必须完全一致。我习惯用下面这组参数dminit PATH/dm/data DB_NAMEDMDB INSTANCE_NAMEDMDB PORT_NUM5236 \ PAGE_SIZE16 EXTENT_SIZE16 CASE_SENSITIVE0 CHARSET1页大小PAGE_SIZE会影响单次IO读取的数据量。如果业务里有大量CLOB字段或者行比较宽16k是比较稳妥的选择如果全是小而频繁的联机事务8k也够用。问题在于主库初始化成16k备库初始化成8k还原的时候马上报错因为备份集里的数据页大小和备库实例不一致。CASE_SENSITIVE决定表名、列名是否区分大小写。习惯上如果业务SQL里写小写表名且希望大小写不敏感就设成0如果是从其他数据库迁移过来的存量SQL最好先确认好原库的规则再定。这个参数同样是初始化后改不了的。dm.ini里重点关注内存和归档相关项。我常用的几个参数如下BUFFER 2048 BUFFER_POOLS 8 MEMORY_POOL 512 ARCH_INI 1具体数值要根据实际内存调整。需要提醒的是BUFFER不要调到物理内存的80%以上否则操作系统层会出现内存回收压力DBA最容易在压测时看到整机swap升高。2.3 账号、端口和应用连接规划建账号这件事看起来基础其实很影响后面的测试效率。SYSDBA是DBA管理账号不适合直接放到应用连接池里跑业务。我在搭建集群前会单独建一个业务账号只授业务需要的查询和增删改权限避免后面做测试时用超级账号误操作。端口方面默认的5236一般是主库备库同样可以用5236应用层通过不同IP区分。主备如果部署在同一台物理机的不同虚拟机上就要把端口错开。这个规划要在初始化阶段定下来等集群搭完再改端口会很麻烦。连接池规划同样重要。备库的最小连接数不要设成0否则高峰期首次建连要经历一次完整的认证和握手延迟会明显拉长。最好让备库连接池保持一个固定的空闲连接数量业务请求到了就能直接用。3. 从零搭建主备同步、守护进程与读写分离集群启动顺序3.1 主库开归档、做基线备份达梦主备同步的基础是归档日志主库必须处于归档模式。在配置读写分离集群之前先改好归档配置否则后面备库追日志会追不上。归档路径需要修改dmarch.ini[ARCHIVE_LOCAL1] ARCH_TYPE LOCAL ARCH_DEST /dm/arch ARCH_FILE_SIZE 2048 ARCH_SPACE_LIMIT 0ARCH_DEST对应的目录必须提前创建并且确保dmserver用户对它有写权限。很多安装过程默认会把这步跳过等到启动守护进程时才发现归档目录不存在。配置完归档后修改dm.ini中的ARCH_INI1重启主库实例。然后做一个全量备份BACKUP DATABASE FULL BACKUPSET /dm/backup/main_full_before_cluster;备份文件建议带上时间戳。传输到备库前用md5sum校验一下不要图省事直接scp过去就完事。我遇到过网络传输中断导致备份集损坏的情况备库还原到一半才报错来回排查浪费了很多时间。3.2 备份还原出备库备库搭建不能直接把主库的数据目录拷贝一份就完事因为数据文件里有活动的重做日志和脏页状态拷贝出来的文件在备库上属于损坏状态。正确方式是备份还原。步骤大致如下在备库安装相同版本的达梦软件安装路径最好和主库一致使用与主库完全相同的初始化参数初始化一个空实例把主库备份集拷贝到备库执行还原RESTORE DATABASE FROM BACKUPSET /dm/backup/main_full_before_cluster;还原完成后把备库实例切到MOUNT状态再设置成备库角色。这里要特别强调初始化参数必须和主库对齐不只是页大小还有EXTENT_SIZE、字符集、是否大小写敏感等。任何一项不一致还原阶段可能不报错但后续守护进程启动或者备库打开时会出现各种诡异现象。数据量很大的情况下全量备份耗时很长可以考虑全量增量的方式。但第一次搭建我建议老老实实做全备把整个链路跑通后再优化备份策略省得在排障时又多一个变量。3.3 配置守护进程和监视器读写分离集群的核心是数据守护机制这里涉及三个配置文件实例的dm.ini、守护进程的dmwatcher.ini、监视器的dmmonitor.ini。dmwatcher.ini的关键是定义守护组和实例信息我提供一份简版模板[GRP1] DW_MODE MANUAL DW_ERROR_TIME 10 INST_RESTART_INTERVAL 60 INST_RESTART_TIMES 3 INST_AUTO_RESTART 1DW_MODEMANUAL表示人工干预为主的守护模式适合先跑通测试生产环境可以根据需要选择更强的自动切换模式。DW_ERROR_TIME是守护进程判定故障的超时时间不能设得太短否则网络抖动就会触发切换。监视器的职责是确认主备状态并在需要时发起切换。启动顺序是关键先启动两个数据库实例确认状态正常再分别启动主备机器上的守护进程最后启动确认监视器。顺序如果反了主备之间会出现互相等待日志不推送备库永远追不上来。3.4 把备库只读能力挂到业务连接上当主备同步状态正常后备库在集群里表现为只读节点。业务侧可以通过两种方式接入应用层分流或集群连接层路由。如果走应用层分流就在连接池里增加一个readOnlytrue的数据源指向备库IP和端口。需要提前在代码里约定好只有查询接口能用这个只读数据源。如果某个查询操作误连到了备库并执行写操作会直接报错应用日志里会出现大量错误这个要提前跟开发团队打好招呼。如果走集群自带路由就需要在连接串里把主备地址都配上并确认SQL类型识别正确。这里最容易掉的坑是长事务。一个事务里先执行update再执行select如果select被自动路由到备库读到的是旧值业务就会出现刚提交的数据消失的问题。后面测试章节我会专门讲怎么验证这个边界。4. 测试验证三步走看路由、看切换、看延迟4.1 先证明备库是只读且同步的搭建完成后第一件事不是压测而是先证明备库是只读且同步的。这个验证非常简单但必须做。在主库上建一张测试表CREATE TABLE t_rw_test(id INT, val VARCHAR(100)); INSERT INTO t_rw_test VALUES(1, master_write); COMMIT;然后在备库上执行查询如果能看到这条数据说明主备同步链路是通的。接下来在备库上执行插入INSERT INTO t_rw_test VALUES(2, should_fail);正常情况下会报只读模式不允许写操作之类的错误。这一步证明备库的只读角色已经生效不会被业务误写入。这里有一个容易被误解的地方备库能查到刚插入的数据并不代表同步没有延迟。要真正看延迟需要在主备两边的系统视图里分别拿当前日志位点做差值写一个小脚本定期记录。延迟超过阈值时自动告警比人肉盯要可靠得多。4.2 模拟主库故障验证切换后的读写表现测试环境里最狠的手段是直接杀掉主库数据库进程kill -9 pidof dmserver这个时候监视器应该会发现主库心跳超时在满足条件后自动把备库提升为新主库。你需要重点观察几件事备库是否在业务可接受的时间内完成角色切换应用连接池是抛异常后自动重连还是持续报无法获取连接切换后的新主库能否正常接受写操作旧主库重启后是否会自动以备库身份重新进入集群日志是否追平。我给测试验收列过一个简单的检查表检查项通过标准主库进程被杀监视器在设定时间内发出告警备库升主系统视图确认角色已改变写请求恢复应用写操作恢复正常无持续报错旧库重新入群日志追平状态变为正常备库数据一致性切换前已提交数据无丢失归档日志完整这里特别提醒切换测试通过之后不要马上正常使用集群。要让旧主库重新以备库身份回到集群确认日志追平再做一次反向切换才能算完整验证。4.3 读性能压测测到瓶颈才叫测过读写分离的核心价值是提升读吞吐。压测不能只测一个能跑要分别模拟不同读写比例。我一般准备两种压测脚本一种是只读的SELECT压测模拟报表查询一种是小事务的INSERT压测模拟联机写入。然后按三种比例跑90%读、80%读、50%读。记录指标包括只连主库时的QPS和TPS读写分离后主库的写TPS、备库的读QPS压测期间主备同步延迟的最大值。示意结果可能像这样压测模式主库TPS备库QPS同步延迟90%读分离前820--90%读分离后1450420018ms压测过程中我遇到过备库CPU先被打满的情况主备延迟开始阶梯式上升。原因不是同步有问题而是备库硬件比主库差。所以压测的目标不只是看备库能承受多少QPS还要看它在你设定的高峰流量下同步延迟是否还保持在可接受范围内。4.4 应用侧的连接池和事务路由验证数据库层面的读写分离配置完成只代表基础设施就绪。真正决定业务是否可用的是连接池和事务边界的行为。要专门设计一个测试用例在一个事务里先执行update再执行同一个事务内的select看看select能不能读到最新值。如果这个select被路由到了备库那么结果肯定是旧数据业务就会报数据不对。解决办法是强制事务内的读也走主库连接。很多ORM框架都有事务路由的开关达梦连接侧也可以指定主备数据源。关键是这个规则要在测试里明确验证而不是靠 reviewer 在代码评审时口头确认。另外还要验证连接池在故障切换后的恢复能力。切换瞬间所有指向原主库的连接都会失效连接池需要能重建连接并重新指向新主库。如果连接池配置里没有启用自动重连或者探测SQL太简单数据库已经切主了应用还是不停用旧连接去请求那故障切换就是失败的。5. 重启、切换与日常维护中容易翻车的几个细节5.1 容易把集群搭砸的三类问题第一类是初始化参数不一致。页大小、字符集不同备份还原阶段不一定立即报错但备库启动或者守护进程拉起来之后就会出现归档无法应用的情况。解决方法是回看dminit参数主备库必须一字不差。第二类是归档目录问题。ARCH_DEST配置的目录不存在或者dmserver用户没有写权限守护进程启动时会一直提示归档失败。很多搭建文档只写了配参数没提醒创建目录和授权这一步最容易漏。第三类是网络和防火墙策略。主备之间、监视器到各节点的端口必须放通。我见过配置完全正确但集群始终不稳定的案例最后发现是监控端口没有放行心跳一直在丢。可以用简单的telnet命令测端口连通性这个动作值得每次变更前都做一遍。5.2 备库延迟的排查三步法备库延迟是读写分离集群最常遇到的性能问题我的排查思路固定三步。第一步看网络。主备机之间ping的RTT是否稳定有没有间歇性丢包。如果网络本身有抖动任何优化都白搭。第二步看归档。主库归档目录的增长速度是否异常如果归档文件堆积严重说明备库根本没有及时把日志拉走。这通常是网络问题或者备库的恢复进程已经停止。第三步看回放。备库的日志回放进程是否处于等待状态备库的磁盘随机IO是否明显慢有没有大查询把备库CPU吃满。常见情况是主库上一个大事务提交后备库回放这个事务需要很长时间这时候备库的读延迟就会瞬时拉高。如果这种大事务很频繁读写分离方案本身就要重新评估。5.3 重启顺序和回切操作备忘集群维护最怕乱序操作。计划内维护的基本顺序是先停应用连接池再停监视器和守护进程最后停数据库实例。启动时顺序反过来先启动数据库确认状态正常再启动守护进程启动监视器最后放量切连接。回切操作是另一个高发事故点。很多DBA在主备切换后发现原主库已经恢复正常就想立刻把它切回主库。但如果没有等原主库日志追平强制回切会造成数据丢失或重复应用。正确做法是先观察原主库作为备库的状态确认日志位点和当前新主库一致后再执行回切。这个清单看起来琐碎但生产上出问题的大多发生在先杀了主库进程但忘了停应用连接池这类顺序错误上。把顺序写进运维操作手册比每次临时想更可靠。最后分享一个我自己的感受。达梦读写分离集群的文档其实写得挺全但真正决定能不能在生产上稳定用的往往是文档里一笔带过的细节初始化参数的一致性、归档目录的权限、防火墙端口、连接池的事务路由。我搭完第一套之后又踩了两次坑才把所有细节补上。如果你也正在搭这套集群希望这些经验能让你少走一点弯路。