干这一行的都有过这种体会:主备环境平时跑得好好的,日常巡检也是绿灯,可真到出故障那天,会发现一堆平时根本想不到的问题。备库同步延迟大、归档日志断档、切换脚本没做过验证、甚至DBA自己都不敢在主库上敲failover命令。所以我一直坚持一个观点——高可用架构不是“搭出来”的,是“练出来”的。这也是我想写这篇DM数据库实时主备故障模拟的初衷。
这篇内容面向的是实际负责达梦数据库(DM8)主备环境运维的同学,不管是刚接触达梦的DBA,还是被安排去搭建和实施主备环境的项目工程师,都可以直接参考。我会把整个故障模拟的过程拆开来讲:从主备架构的基本原理,到演练前要做的准备,再到真实的故障场景操作步骤,最后是常见问题和排查思路。内容比较长,但都是能直接用上的东西,建议先收藏再慢慢看。
1. 为什么要把“好端端”的主备环境搞出故障来
很多人觉得主备环境搭好就完事了,平时主库正常干活,备库安安静静跟着同步,根本没有必要主动去破坏它。这是非常危险的想法。数据库高可用体系里,“故障切换”和“日常同步”是两码事,前者才是真正决定RTO和RPO的关键环节。
我先说一个自己遇到过的例子。某次客户环境做月度演练,主备集群用的是达梦DM8实时归档模式,备库状态看监控是正常的,主库一天的归档量也不大。可演练一开始,主库进程杀掉之后,备库迟迟没能在预期时间内完成接管,最后发现备库的redo日志应用一直卡在一个大事务上,平时同步稍微滞后一点根本看不出来,但切换时这就成了致命问题。
所以故障模拟的核心价值有几点:
- 验证切换链路是否真的可用。守护进程、监视器、手动切换命令、应用连接串的failover配置,这些环节任何一个有问题,故障发生时都会掉链子。
- 验证RPO是否可接受。实时主备不等于零丢失,某些模式下最多可能丢一个归档日志。你到底能接受丢多少数据,必须实测出来。
- 验证回切路径是否顺畅。很多团队只做“主切备”,从不做“备切主”。等主库修好了,想切回去的时候才发现原有的主库状态不对,或者归档补齐有问题,直接抓瞎。
- 验证人的操作熟练度。这个最容易被忽视。故障处理是有时间压力的,如果操作者平时没练过,真到半夜故障时一边查手册一边敲命令,RTO基本不可能达标。
DM数据库的主备架构有几种常见形态:实时归档主备、自动归档主备、读写分离集群、共享存储集群(DSC)。这次故障模拟我以最常见的“实时归档主备”为对象。之所以选它,是因为这套架构部署量最大,而且它本质上和Oracle的Data Guard非常像,理解了一个,很多思路可以平移过去。实时归档模式下,主库产生的redo日志会通过MAL系统实时传递到备库,备库收到后立即应用,主备数据保持一致。守护进程(dmwatcher)负责监控主备状态,监视器(dmmonitor)负责在必要时发起自动或手动切换。
故障模拟要做的,就是在这种架构下人为制造异常,验证整个生命周期是否经得起考验。
1.1 主备同步的基础概念要知道
动手演练之前,有几个基础概念必须心里有数,不然后面操作容易看懵。
redo日志和归档日志。主库上的每一个事务提交都会先写redo日志,redo日志写满后触发归档,生成归档日志。实时主备的核心逻辑就是:redo日志每次切换或写入时,通过MAL通道实时传到备库,备库应用redo和归档,让数据保持同步。可以把它理解成“流水线”——主库不断产出日志,备库不断消费日志,任何一个环节卡住,同步就会滞后。
备库的两种应用模式。达梦实时主备里,备库可以按需配置为realtime模式或timely模式。realtime模式是实时应用收到的redo日志;timely模式则强调更严格的一致性,备库在应用日志时会更谨慎。还有一种是本地归档+日志应用,适合用来做时间点恢复。我们做故障模拟时,一般建议用timely或realtime,因为这两种才能真正做到“分钟级甚至秒级接管”。
守护进程和监视器的角色。守护进程跑在主备库上,负责收集本机数据库状态并上报给监视器;监视器可以理解为“指挥中心”,它能看见整个主备集群的状态,执行switchover(计划内切换)或failover(故障强制切换)。没有监视器,你是没法一键切换的,只能手工登录到备库执行命令。我们的演练默认是有监视器的环境,因为生产上不可能裸奔。
1.2 故障模拟和其他高可用方案的区别
很多接触过Oracle的人会问:达梦主备和Oracle Data Guard是不是一回事?机制上确实很像,都有主备、日志传输、应用、切换这套流程,但达梦有几点不同需要注意。
第一,达梦的守护进程和监视器是一套独立的进程体系,配置和启动顺序有严格要求,乱启动会直接导致监视器看不到集群状态。第二,达梦的MAL系统配置涉及多个配置文件,任何一个节点配置不一致,日志传输就起不来。第三,达梦的failover命令和Oracle不太一样,执行前对两端状态有更严格的约束。这些差异,只有真正动手做过演练才能体会。
这个章节不是让你去背概念,而是先建立一个全局认识。接下来我们进入正题:演练前需要准备什么。
2. 演练准备:动手“搞破坏”之前,先把底线画好
故障模拟不是上去就把数据库杀掉,它本质上是一次有预案的破坏性测试。准备工作做得好不好,直接决定演练过程和结果分析有没有参考价值。
2.1 准备一套尽量接近生产的测试环境
如果你手头有生产环境的备库,理论上可以直接在备库上做验证,但我强烈建议先用测试环境来一遍。原因很简单:故障模拟中会出现各种不可控的意外,比如网络中断后恢复异常、归档断档需要补日志、主库修复后状态不对需要重建,这些操作在生产上做,风险是不可逆的。
测试环境的搭建可以走标准的达梦主备流程:
- 准备两台Linux服务器,一台主库一台备库,操作系统版本一致,DM8数据库版本一致,最好连补丁版本都一致。
- 主库安装数据库实例,开启归档(dmarch.ini),配置MAL系统(dmmal.ini),备库通过备份恢复方式搭建,确保两端的数据结构完全一致。
- 配置守护进程(dmwatcher.ini)和监视器(dmmonitor.ini),测试环境可以配一个非确认监视器,生产上建议至少两个监视器,避免监视器自身单点。
- 启动顺序有讲究:先启动主库和备库实例,然后启动守护进程,最后启动监视器。监视器里执行
show global info,能看到两端状态处于正常同步,才可以开始演练。
测试环境的数据尽量模拟生产形态:有常规业务表、有索引、有存储过程、有大事务表,如果能导入一份业务相关的dmp数据就更好了。数据越接近真实,切换时的耗时和资源使用越有参考价值。
2.2 先给环境留几条“后路”
既然是故意制造故障,就一定要先确保即使演练翻车,也能恢复原状。我的习惯是动手前先做三件事:
- 全量备份主库。用
dmrman或者DMRMAN的命令做一次在线备份,万一演练过程中数据文件损坏,可以快速恢复基线。 - 记录当前主备的日志序列号。查一下主备库当前的redo日志序号和归档日志序号,记录下来。演练结束后,通过对比日志序号能快速判断同步是否有断点。
- 检查两端的时间同步。如果主备库时间差太多,日志传输的时间戳会对不上,造成莫名其妙的同步中断。用
ntpdate或者chronyc校准一下再开始。
额外说一句,我习惯在演练开始前建一张“标志表”,比如:
CREATE TABLE FLAG_TEST(ID INT, FLAG_TIME DATETIME); INSERT INTO FLAG_TEST VALUES(1, SYSDATE); COMMIT;这表看着简单,但演练时很有用。切换完成后,登录备库查这张表,能直接看到切换前提交的数据是否已经同步过来了,比看监控和日志直观得多。
2.3 定好演练场景和预期目标
准备环境只是第一步,更重要的是定好演练场景。我自己常用的故障场景有以下几类,这次挑几个有代表性的展开讲:
- 主库实例进程崩溃(模拟掉电、数据库异常宕机)。
- 主库数据文件误删或文件系统损坏。
- 备库节点掉线后恢复,验证主库不受影响以及备库自动追平。
- 主备网络心跳断开,验证脑裂防护机制。
每一个场景都要有明确的预期:什么时候触发切换、切换耗时多少、数据有没有丢、切换后应用能否正常读写、如何回切。没有预期目标,演练变成了瞎折腾,复盘的时候也说不清到底有没有达到高可用要求。
3. 核心实操:四个典型故障场景的模拟全过程
准备工作做完,下面进入真正的“破坏”环节。我会按每个场景的完整过程来写,包括触发操作的命令、观察到的现象、验证的方法和关键注意事项。这些内容都是我实际演练中跑过或见过别人跑过之后总结出来的,直接抄作业是没问题的。
3.1 场景一:主库实例进程崩溃
这是最经典的故障模拟,模拟的是主库突然宕机,比如服务器掉电、操作系统卡死或者数据库进程被误杀。重点验证的是备库能否快速接管、切换后数据是否完整。
第一步,触发故障。不要用SHUTDOWN NORMAL或SHUTDOWN IMMEDIATE,因为那属于正常关闭,主库会及时通知守护进程。要模拟“瞬间死亡”,用强制终止进程的方式:
# 找到主库的dm进程 ps -ef | grep dmserver # 强制干掉进程,模拟瞬间崩溃 kill -9 <dmserver_pid>如果没有自动切换配置,这一步执行后,监视器里会看到主库状态变成ERROR或UNKNOWN,守护进程不断尝试重连。这时不要慌张,这是正常的。
第二步,在监视器里观察状态:
show global info;重点关注几个字段:主库状态是否异常、备库状态是否还是STANDBY、日志发送是否中断。如果配置了自动切换,备库可能会自动变成PRIMARY,这取决于你的故障自动切换策略。
第三步,手动执行failover切换。即使有自动切换,我通常也会用手动命令再验证一次,因为生产上很多时候自动切换会因为各种条件不满足而没有触发:
-- 在监视器里执行 failover 组名.备库名;执行过程中,监视器会尝试把备库提升为新的主库。命令执行完,再登录新的主库查询状态:
SELECT DATABASE_MODE FROM V$INSTANCE;结果应为PRIMARY。同时查一下最开始建好的FLAG_TEST表,看切换前那条记录在不在。
这里要特别提醒一个坑:failover之后,原来的主库如果没有被完全清理,状态可能处于UNKNOWN或RECOVERY状态。不要急着把它的数据拿回来直接用,后续要重新规划角色——要么作为新备库重新搭建,要么彻底清理后重建。我之前见过有人failover后直接把老主库强制启动,结果同一套环境出现两个PRIMARY,业务写入直接错乱,教训非常深刻。
3.2 场景二:主库数据文件误删
这个场景模拟的是文件系统层面的损坏,比如运维误删了数据文件,或者磁盘坏道导致数据文件不可读。
操作上,我推荐在数据文件目录下手动改名或删除一个用户表空间的数据文件,而不是动系统表空间的文件。动系统表空间属于另一个极端场景,影响面太大,不利于演练后的快速恢复。
假设我们要删除用户表空间 TS_APP 对应的数据文件:
# 进入数据文件目录 cd /dm8/data/DAMENG # 找到TS_APP的数据文件,改名模拟损坏 mv TS_APP01.DBF TS_APP01.DBF.BAK然后尝试在主库上访问这个表空间中的表,让错误暴露出来:
-- 在主库执行 SELECT COUNT(*) FROM APP.USER_ORDER;此时大概率会收到文件读写错误,因为DM在做物理读时发现文件找不到了。观察主库日志,会出现大量的IO错误记录,但备库此时通常不受影响,因为备库走的是日志应用,和数据文件物理路径没有直接关系。
在这个场景中,要验证的关键点是:主库“坏了”之后,业务是否能切到备库继续跑。操作流程和场景一类似,在监视器里执行failover即可。但有一点和进程崩溃不同:切换完成后,原来的主库因为有物理文件缺失,已经不能简单重启拉起来,只能通过新备份的方式重建,或者先把误删的文件恢复回去再处理归档追平。
我在复盘这个场景时经常强调:数据文件被误删,是“高可用”最尴尬的敌人。备库虽然能接管业务,但如果平时备份策略不到位,等你想把旧主库修好时,才发现备库没有全量备份,那就只能靠日志慢慢追,RTO直接拉长到无法接受。
3.3 场景三:备库掉线后再恢复上线
生产上备库掉线也是常事,比如备库服务器要重启、网络抖动导致MAL连接中断、备库磁盘空间满了导致归档应用暂停。这个场景验证的不是“切换”,而是“追平”的能力——备库脱离一段时间后,能否自动把缺失的日志补回来,回到实时同步状态。
触发方式很简单,直接停掉备库实例:
# 在备库服务器上执行 DmServiceDMSERVER stop此时主库对外服务完全不受影响,业务继续正常写入。注意观察主库的归档日志目录,会发现文件在不断增加,但因为备库不在线,日志传输中断,主库归档目录占用会持续增长。如果磁盘空间有限,这里就是一个很现实的隐患。
过一段时间(建议至少让主库产生几十个归档日志),重新启动备库实例。正常情况下,备库启动后会自动从断点处请求主库补发缺失的日志,不需要人工干预。在监视器里查看状态,备库会经历RECOVERY状态到OPEN状态的转变。登录备库查FLAG_TEST表,主库最新写入的数据应该已经同步过来。
这个场景里最容易出的问题就是“归档断档”。如果主库的归档日志因为磁盘空间清理被删掉了,备库就无法补传,只能重新做一次全量恢复。所以日常运维中,主库的归档日志保留策略必须和备库同步消耗速度匹配,至少保留一天以上,不要一归档就清理。
3.4 场景四:主备间网络心跳中断
这个场景最刺激,也最容易让人在演练时翻车。主备库之间的网络断开后,备库不知道主库是否还活着,主库也不知道备库状态如何,两边都可能认为自己应该接管服务,形成“脑裂”。达梦的守护进程设计上会尽量避免脑裂,但前提是配置正确。
触发方式可以这样:在主库和备库之间的网络链路上做隔离,或者直接用防火墙阻断MAL端口。
# 在主库服务器上,阻断到备库MAL端口的通信 iptables -A INPUT -s 备库IP -j DROP操作后观察监视器,主备之间会断连,守护进程会报告网络异常。这里关键要看两点:第一,备库是否会因为没有收到主库的心跳而自动提升为PRIMARY;第二,主库端是否会误以为备库全部下线而出现异常行为。
默认配置下,达梦不会在网络中断瞬间就自动切换,而是会经过一个判断超时窗口,避免主库只是短暂卡顿就触发切换。如果你的环境在演练中发现备库“抢主”很快,或者两个节点同时变成PRIMARY,那说明你的超时参数和仲裁策略需要调整。
这个场景的演练结果往往不是“切换是否成功”,而是“切换决策是否符合预期”。比如网络中断30秒后,系统决定不切换,等网络恢复后自动重新同步,这算正常;如果网络中断30分钟,备库依然迟迟不切换,那就要考虑是否需要调整超时参数。生产上,我倾向于让网络断开的场景由人工介入决策,因为自动切换在网络抖动时的误判率并不低。
4. 故障模拟中常见的问题与排查方法
故障模拟的价值不仅仅在于“成功切换”这个结果,更多在于演练过程中暴露出的问题。我把自己见过、处理过的几类高频问题整理成了一张表,看的时候可以对号入座。
| 问题现象 | 可能原因 | 处理思路 |
|---|---|---|
| 备库无法提升为PRIMARY | 备库缺少关键日志,或备库状态不是STANDBY | 检查归档是否连续,必要时重建备库 |
| 切换后FLAG_TEST数据缺失 | 实时归档有延迟,或备库应用卡顿 | 检查备库redo应用状态,确认RPO |
| 监视器看不到集群状态 | 守护进程未启动,或MAL配置不一致 | 按顺序重启守护进程和监视器 |
| 主库归档日志持续增长,备库不消费 | 备库掉线,且主库归档保留策略过短 | 清理前先判断备库是否可以补传 |
| failover后老主库仍显示PRIMARY | 老主库未清理,或网络恢复后角色冲突 | 禁止强制启动老主库,重建或重启后确认状态 |
| 切换后应用无法连接数据库 | JDBC或ODBC连接串没有配置failover | 检查连接串,配置多地址failover |
| 主备恢复后同步断档 | 归档日志已被清理,无法补传 | 使用全量备份重建备库 |
| 回切时原主库状态异常 | 原主库存在未恢复日志或物理文件问题 | 先恢复原主库到一致状态,再执行switchover |
下面挑几个典型问题再展开说,因为光看表格不一定知道具体怎么处理。
4.1 备库无法切换为Primary的原因分析
这个问题的出现频率非常高。某次演练中,备库状态看起来一切正常,但执行failover时报错,说当前备库不允许切换。查日志发现,备库的redo日志应用进度落后了主库一大截,原来实时归档模式下备库虽然“在线”,但应用日志的进程因为一个超大事务卡了很久,表面上是同步状态,实际数据滞后严重。
排查思路是:查看主备库的日志应用进度,用V$RLOG和V$ARCH_STATUS等视图对比两端的日志序列号,确认备库是否已经应用到最后一条日志。如果差得远,就不要硬切换,先让备库追平。这个场景也说明了为什么演练前要在非高峰期进行,避免大事务干扰。
4.2 切换后应用连不上数据库
有一次演练,切换整个过程非常顺利,备库已经变成PRIMARY,但业务应用的连接全部报错,排查下来发现是应用连接串只写了原主库的IP,没有配置备库地址。达梦JDBC驱动支持多地址连接串,生产上建议从一开始就配置好,别等切换了再改应用。
示例连接串配置可以参考:
jdbc:dm://主库IP:5236,备库IP:5236?failover=on&autoReconnect=true这样主库故障时,应用会自动尝试连接备库地址。当然,如果应用层用了中间件或连接池,也要同步检查连接池的故障转移配置,这些不是数据库侧能解决的。
4.3 回切失败的常见原因
演练中“切过去”很开心,“切回来”却经常卡壳。回切的前提是原主库已经修复好,状态和新的主库一致。有人直接启动老主库,结果它因为还保留着PRIMARY标识,和新主库产生角色冲突。正确做法是先把老主库启动到MOUNT状态,然后执行转换为STANDBY的操作,再启动守护进程让它重新加入集群,最后再执行一次计划内的switchover把主库角色切回老主库。
回切命令我建议用switchover来做,流程是可控的。如果老主库数据落后太多,switchover会拒绝执行,这时需要先通过日志修复让老主库追平。我在演练中至少遇到过两次,因为老主库在故障期间还有其他独立写入,导致角色切换时日志冲突,最终只能靠备份重建,耗时很长。
5. 复盘与收尾:让演练不止于演练
所有故障场景都跑完之后,最重要的工作就是复盘。不要觉得切换成功了就万事大吉,整个演练过程的每个决策点、每个耗时环节、每个异常告警,都值得仔细过一遍。
我习惯用一张清单来做复盘,你可以直接拿去改:
- 故障发现到切换启动,花了多长时间?这个时间有没有超出预期?
- 切换命令从发起到数据库角色变更完成,花了多长时间?
- 切换完成后,业务验证是手工做的还是自动做的?耗时多久?
- 有没有出现备库追不上日志、归档断档、连接串失效这些问题?
- 故障期间监控告警是否及时?告警信息是否准确?有没有漏报误报?
- 演练结束时,主备状态是否完全恢复正常?同步是否实时?
- 演练过程中,哪些操作是临时查手册才做的?把这些操作沉淀成SOP。
- 演练后,配置、脚本、备份策略有哪些调整项?
补充分享一个我在复盘时经常用的小技巧。演练开始前,我在两张表记录时间点:一张是业务侧的时间戳(FLAG_TEST),一张是数据库日志中的切换时间。结束后把两张表的时间点放在一起对比,就能还原出最接近真实的“故障时间线”。比如故障发生在10:00,守护进程发现主库异常用了30秒,failover执行用了45秒,应用重新连上新主库用了1分钟,最终RTO就是2分15秒。这个数字比任何监控大屏展示的都直观。
再讲一点关于RPO的体会。使用实时归档模式时,很多人以为数据完全不会丢,但实际演练中发现,如果在主库崩溃瞬间恰好有redo日志还没来得及传输,这部分数据就会丢失。具体丢多少,要看日志传输的实时性和网络延迟。我见过一个极端案例,大事务产生的redo巨多,传输滞后了十几秒,崩溃后直接丢了十几秒的数据。所以演练之后,一定要明确记录这个“丢数窗口”,并把它同步给业务方,让对方评估可接受性。
最后说一个我个人一直坚持的习惯:故障模拟不是一次性工作,建议至少每季度做一次,并且在每次数据库版本升级、服务器迁移、网络架构调整后都要补做一次。另外每次演练,尽量让团队里不同角色轮流担任“故障指挥官”,DBA、运维、应用负责人各自负责一块。毕竟真实故障发生时,你的团队有没有形成肌肉记忆,比任何架构文档都靠得住。
演练结束,大家一起把检查清单签完字,把产生的新配置项和优化任务落实到下一迭代,这才算真正完成了一次有价值的主备故障模拟。