fio压测误操作致达梦主备集群故障与备份链修复复盘

fio压测误操作致达梦主备集群故障与备份链修复复盘 主备同时告警、备份接连失败——如果不是亲眼看到我真想不到一条fio命令能把信创库环境炸成这样。我是惜分飞傧干了十多年数据库运维这几年在国产信创环境里做替换改造接触最多的就是达梦数据库。这篇复盘记录一次在信创操作系统上对达梦主备集群做磁盘压测时因为fio不当操作引发的主备库故障以及备份链路连带损坏的处理全过程。写这篇东西的目的很简单这类误操作随时可能发生在任何一个信创数据库运维同事身上而且破坏往往不是单点的是主库、备库、备份三线一起炸。文章里会拆解fio的破坏路径、主备库的恢复步骤、备份链路的修复方法以及一堆只有踩过坑才知道的细节。无论你现在是在做信创迁移还是已经在维护达梦、人大金仓这类国产库这篇都值得看完。1. 事故现场主备库和备份三线告警1.1 项目背景达梦主备上的磁盘压测当时项目背景是一套典型的信创环境数据库用的是达梦DM8操作系统是国产信创操作系统麒麟系两台节点组成主备高可用集群承载着业务系统的核心读写。其实这套库上线后跑得挺稳定问题出在一次存储性能验证上。存储这块需要出性能测试报告厂商建议在主机上用fio直接对磁盘做读写压测看IOPS和带宽能否达到设计要求。按常规理解fio是通用压测工具跑一下物理磁盘的裸性能好像没什么风险。坏就坏在当时的磁盘规划数据库的数据目录、重做日志目录、归档目录恰好都分布在同一个存储挂载点上而压测人员习惯性地在数据库所在主机的目标挂载路径下执行了fio。压测从下午开始跑了几分钟数据库监控平台就开始刷告警。我接到电话时其实已经有两拨告警等在处理队列里。1.2 主备库故障现象主库这边最直观的现象是业务侧查询变慢大量会话堆积数据库日志里开始出现数据页读取异常的报错。部分写事务直接失败应用端报ORA类错误从Oracle迁移过来的应用保留了不少O开头错误码习惯其实底层已经是DM8语法现场业务几乎是半瘫痪状态。备库更严重归档传输中断备库与主库之间的日志断层迅速变大。用达梦的术语说就是出现了类似Oracle主备切换里常见的那种“resolvable gap”问题——日志缺口存在但一时无法自动追平。我查了守护进程日志从某个时间点开始备库就再没有成功应用过新的归档日志。当时第一反应是存储层出了问题因为数据库本身没有变更操作所有异常都发生在压测开始之后。但没想到问题比存储慢更严重后续备份任务也陆陆续续报了失败。1.3 备份任务连环失败达梦的备份策略是每周日全备、每天差备和归档备份。事故当天正好是差备日凌晨的备份任务失败监控没有立刻暴露出来备份失败告警配置漏掉了这个后面单独讲等我们发现时已经积累了好几个失败的备份集。首先报错的是备份目录空间不足。fio压测时在主库数据目录同挂载点下生成了一个超大的测试文件这个文件直接把可用空间吃光了。备份进程写不进去自然失败。其次即使腾出空间旧的备份集在校验阶段也报文件头异常无法继续作为恢复基准。还有些归档日志文件因为fio的写入干扰校验值对不上导致备份脚本中途中断。主备库已经处于亚健康状态备份链路又断了这意味着一旦主库彻底损坏我们连“回滚到昨天”这种最后手段都没有。这种情况下只能先稳住局面再逐步恢复。2. 根因拆解fio怎么一步步弄坏主备和备份2.1 fio的正常使用与危险操作边界fio的全称是Flexible I/O Tester是Linux下最常用的磁盘性能压测工具之一可以非常精细地控制IO引擎、块大小、队列深度、读写模式、压测时长等参数。数据库部署前、存储上线前、硬件验收时用它来摸底磁盘极限性能是再正常不过的操作。fio本身没有恶意坏的是用法。它有两种非常危险的误用场景第一种直接把filename参数指向裸设备或者数据库文件然后在上面跑rwwrite或rwrandwrite。比如fio -filename/dev/sdb1 -direct1 -iodepth32 -rwrandwrite -ioenginelibaio -bs4k -size10G -numjobs1 -runtime300 -group_reporting这个命令执行后/dev/sdb1上的数据会被随机写覆盖如果它是数据库数据盘数据文件基本就是被拦腰砍断了。第二种看起来无害但同样致命就是不加filename、不加directory直接在某个目录下跑fio。fio会在当前工作目录自动生成名为jobname.0.0的大文件然后疯狂写入。如果这个目录恰好是数据库数据文件或归档日志的挂载点文件系统被写满只是时间问题数据库写重做日志失败后整个实例都可能hang住。我们这次事故的触发路径更隐蔽压测命令是加了directory指向挂载点的本意是只生成压测文件不碰数据库文件但fio这样写等于把一个超大文件持续写入正在被数据库使用的文件系统分区最终把分区写满同时IO风暴让数据库完全没有喘息空间。2.2 主库被破坏的两条路径主库受损其实有两条独立路径叠加这也是为什么这次故障这么棘手。第一条路径是IO资源被耗尽。fio压测产生的IO负载远远超出正常业务负载存储LUN的队列深度被打满数据库写数据文件、写重做日志的IO请求全部在排队。数据库这种系统对IO延迟极其敏感尤其是重做日志写入一旦延迟过高提交事务就无法完成大量会话开始堆积数据库进入假死状态。表面看是数据库故障底层其实是IO饥饿。第二条路径是文件系统空间被污染。fio生成的超大测试文件占满了整个挂载点数据库在运行过程中需要不断写新日志、扩展数据文件、写归档结果磁盘没有空间了。一部分数据页写入失败还有一部分数据页被异常覆盖。达梦在读取这些受损数据页时报出了数据块头损坏、校验不和等典型的物理损坏错误。这两条路径同时发生直接导致主库从正常状态跌落到“部分数据文件损坏实例运行不稳定”的中间状态。这个状态最麻烦因为不是一键启动就能恢复得先处理受损的数据文件。2.3 备库同步中断的传导逻辑达梦主备集群的同步机制核心是主库不断生成重做日志和归档日志通过网络传给备库备库在收到后应用日志保持数据同步。备库正常运转的前提是它能持续收到主库传来的完整、一致的日志流。当主库的文件系统和存储IO被fio搞乱后传导链条是这样的主库无法及时生成和传输完整的归档日志部分已经传过去的归档日志本身可能就是损坏的因为底层数据块在写入时已经受了影响备库应用日志时发现数据页校验失败或者应用进程读到异常数据最终中断。备库一旦中断主备之间的日志缺口就会持续拉大。这种gap在Oracle里叫resolvable gap在达梦里也是一样需要人工介入处理。备库越落后后续想要重新追平的成本就越高。更难受的是正因为备份链路也断了我们没有干净的历史基线可以用来快速追平备库。只能先救主库再重建备库等于两条腿都断了重接。2.4 备份失败的连锁链条备份链路在这次事故里是最容易被忽视但后果最严重的环节。表面看备份失败就是磁盘满了、文件写不进去。实际上fio对备份的破坏有三个层次。第一个层次是空间层面的fio大文件占满备份目录所在分区导致任何新的备份集都无法落盘。第二个层次是校验层面的备份集里某些数据文件已经被写坏文件头的校验值和记录不一致备份软件在扫描备份集时直接判定备份集不可用。第三个层次是链条层面的因为备份集失败后续差异备份、归档备份也全部中止整个备份链从某个断裂点开始彻底断掉。备份链断裂的危害在于就算你后续修复了主库一个没有连续备份链的环境依然无法应对下一次故障。备份的可用性不是看你有没有跑备份任务而是看关键时刻能不能真正恢复出来。这场事故让我对这句话的体会深到骨子里。3. 主备库恢复实操从止损到重建备库3.1 第一时间止损与现场信息收集事故处理的第一步永远不是急着恢复而是止损。我们第一时间让压测进程停掉把fio生成的测试文件隔离出来确认数据目录所在分区的可用空间恢复到一个安全阈值。同时通知应用侧暂停写入流量让数据库从IO饥饿中缓过来。这一步如果不做后面的一切恢复操作都可能在继续恶化的存储状态下进行。随后开始收集现场信息。数据库告警日志、守护进程日志、系统messages日志、存储侧监控曲线全都留档。这里额外强调一点不要等到重启或恢复之后才去翻日志很多关键报错信息在数据库运行状态变化后就会被覆盖掉先备份日志文件再动手这是保命习惯。3.2 判断故障范围哪些能救哪些只能重建主库的故障范围不是看一句话结论而是要逐层确认。我们通过查询数据库状态和动态性能视图把受损对象分成了三类第一类是可以直接重建的比如临时文件、已损坏且无业务价值的归档日志第二类是需要从备份恢复的比如部分数据文件第三类是需要重点评估的比如控制文件和重做日志。最终判断下来主库的重做日志文件没有发生物理覆盖这是不幸中的万幸。控制文件也基本完整。主要受损的是两个数据文件、部分归档日志以及临时表空间。数据文件有问题意味着数据库无法正常对外服务必须尽快处理。3.3 主库恢复处理面对主库受损的数据文件恢复优先级非常明确优先清理可直接重建的临时文件让实例先能启动数据文件则从备份集中恢复。但是这里有前提——必须确认哪一个备份集是最新的干净基线。我们翻遍了最近几天的日志找到一个异常发生前的全备同时把全备和异常发生前的归档日志拼接起来确认这个组合可以覆盖绝大部分已提交事务。恢复方案我用了达梦自带的dmrman工具类似这样dmrman RESTORE DATABASE FROM BACKUPSET /备份目录/cm_bak_2025_xxxx; RECOVER DATABASE FROM BACKUPSET /归档备份目录; ALTER DATABASE OPEN;实际执行时命令细节要按版本对应的帮助来确认但整体思路是恢复数据文件到一致性状态再通过归档日志把数据推进到尽可能接近故障点的位置。这一步做完后主库可以正常打开业务读流量先恢复。写流量没有立即切回因为备库还没有重建主库一旦再次出现问题我们就没有冗余了。先确保主库稳定运行再处理备库。3.4 备库重建与同步恢复主库恢复后摆在前面的选择是继续尝试追日志还是直接重建备库。按理说如果归档缺口不大追日志成本更低。但我们评估后发现压测对归档文件的坏影响是零散存在的强行追日志可能在中间某个归档上再次中断到时候排查成本更高。所以直接选了重构备库。重建备库的步骤本质上是“把主库当前的状态克隆一份给备库”。我用主库做了一次新的全量备份将这个备份集拷贝到备库节点在备库上执行恢复然后用主库的归档继续追平。核心操作大致如下主库执行全量备份确保备份集处于一致状态。在备库上把原实例目录清理或归档隔离避免旧数据文件干扰。用dmrman把备份集恢复到备库的数据目录。重新配置MAL系统相关参数和守护进程配置确保主备两端的实例名、端口、路径一致。启动备库启动守护进程观察主备日志同步状态。这里有一个特别容易踩的坑备份集恢复之后备库的DB_MAGIC会和主库不一致如果不更新DB_MAGIC信息主备之间根本无法建立同步关系。当时我用RECOVER DATABASE UPDATE DB_MAGIC一类的命令处理具体语法以你环境版本的dmrman提示为准然后重启守护进程。备库追平后我通过查询主备两端的LSN差距做验证确认日志断层不再扩大备库应用日志正常推进。再执行了一次主备切换演练把备库提升为主库角色运行几分钟确认切换链路正常才让业务写流量全面恢复。这一步绝对值回票价因为主备切换演练暴露了一个MAL配置参数没同步到位的问题如果真等到故障切换才发现后果不堪设想。4. 备份链路修复与可用性验证4.1 备份失败根因确认主备库恢复后备份链路依然处于不可用状态。我首先把前几次失败备份日志拉出来逐条看发现失败原因其实有好几个叠加项不是单一问题。第一是备份目录空间不足。fio遗留下来的压测文件虽然被清理了但备份任务多次失败导致残留的半成品备份集还挂在目录里占着空间。第二是旧备份集校验失败。某些备份集在备份过程中就已经经历了数据页异常备份文件本身不可信留着只会误导后续恢复判断。第三是归档不连续老归档缺失导致备份链无法衔接。排查结论用表格整理会比纯文字直观现象根因定位处理方式备份任务报空间不足fio文件占满分区、失败任务残留半成品备份集清理临时文件、删除不可用的半成品备份集校验不通过备份期间数据页受损坏影响删除损坏备份集重建基线备份归档备份中断归档序列存在缺口从主库重新生成并补齐归档4.2 重建备份基线备份基线的重建我坚持了一个原则宁可现在多花时间也不能给未来留隐患。先清理完备份目录空间再手动触发一次全量备份生成一套全新的全备。这套全备做完后紧接着连续观察了几天增量备份和归档备份是否正常。同时把备份监测脚本补上加了三个关键判断备份任务是否成功结束、备份集是否能正常校验、备份目录剩余空间是否低于阈值。这三个判断是之后所有备份监控的底线。备份保留策略也做了调整以前是全备只留两份实际恢复时才意识到风险太大。如果其中一份不可用你根本来不及找另一份顶上去。调整后是全备至少保留三份且必须有一份放在独立存储或异机目录。磁盘成本就那么多但关键时刻一份可用备份的价值远超存储成本。4.3 恢复演练验证备份真能用备份是否可用不经过恢复演练就是空谈。这是我处理完这次故障后最坚持做的一件事。我找了一台空闲测试主机把刚生成的全备恢复到测试实例启动数据库模拟了几类典型查询和事务操作。演练过程中还真发现了一个问题恢复出来的实例表空间状态正常但某张业务大表的索引段存在逻辑损坏。这个损坏在主库当前运行环境下没有暴露因为数据库还没访问到那一部分数据页。如果在真实生产恢复场景下业务一查这张表就会报错。后来我对照主库对象逐一检查发现这个索引可以重建问题解决。这事给我的触动挺大。生产备份经常只关注备份任务是否跑完却从不关心恢复出来能不能用。备份系统是从成功备份到成功恢复的完整闭环任何一环没有验证都不能算真正的可用备份。5. 避免重蹈覆辙给信创环境DBA的几条建议5.1 fio压测红线清单吃过这次大亏我把fio的使用红线整理成了几条硬性规定团队里所有人都必须遵守。第一数据库生产节点上严禁直接运行针对数据盘、重做日志盘、归档盘的随机写压测。如果确实要验证存储性能优先选择未挂载的空闲设备或者通过存储侧快照克隆一块独立LUN做测试。第二任何fio命令执行前必须人工确认filename和directory两个参数指向的不是数据库相关路径。这个确认不能靠口头要在执行前把命令打印出来贴到操作记录里。第三fio压测期间必须有专人盯守磁盘空间和IO状态一旦发现空间使用率快速上涨立即终止。第四所有压测操作必须通过变更审批流程搞清楚影响面再动手。这四条听着像废话但实际操作中总能碰到有人图省事。信创环境下大家对国产数据库的运维习惯还在磨合期一些以前在Oracle、MySQL上积累的“常规动作”到了新环境可能完全不可用。fio就是典型例子它不是不能在这个环境里用而是必须换个更严格的用法来用。5.2 主备库与备份的关键监控项这次事故里报警配置漏掉了备份任务失败告警导致问题被发现时已经积累了多个失败任务。我后来把主备库和备份的监控项重新梳理了一遍核心就几条但每一条都能提前暴露问题主备同步状态备库是否处于正常的归档应用状态主备LSN差距是否持续增长。这条能提前发现gap问题。归档日志连续性归档目录是否有缺失文件归档生成频率是否异常。数据文件完整性定期做数据文件校验尽早发现文件头损坏、校验不一致问题。备份任务结果备份成功或失败必须有独立告警不能只靠人工看日志。磁盘空间水位数据目录、备份目录、归档目录的空间使用率要设置独立阈值比如超过80%就预警超过90%必须介入。监控不是为了事后复盘而是为了在问题还没放大的时候发现它。这几个指标看着基础但很多团队恰恰是基础指标漏掉最后被拖进大故障。5.3 应急预案与演练这次事故之后我主导把备份恢复演练变成了固定动作每季度至少做一次。演练内容包括把最新全备恢复到测试环境验证数据库能否正常启动执行一次主备切换验证备库能否在有限时间内接管业务随机挑选一个备份集做恢复校验确认备份文件本身没有问题。演练这件事管理层一开始觉得是浪费时间直到后来有一次存储控制器故障我们靠着季度演练积累的熟练度在两小时内完成了备库接管和业务恢复才没人再质疑。很多数据库事故之所以处理时间长不是因为故障本身多难而是没有预案、没有演练、没有验证过的恢复路径。个人体会是国产信创数据库这个方向的运维代码、命令都可以慢慢学但故障处理的思维和底线意识必须要从每个真实事故里沉淀。备份恢复可不可用平时看不出来灾难来的时候就是生死线。这篇复盘如果能让你在下次执行fio、下次调整备份策略、下次主备切换前多停顿几秒想清楚后果那这个教训就值了。