sigkill(kill -9)是唯一能真实模拟断电式异常终止的软件手段,它强制中断数据库主进程、丢弃未刷盘数据并触发crash recovery,但必须精准杀主进程、启用fsync/synchronous_commit、校验recovery完成状态且隔离环境常态化执行。

断电测试不能真断电,kill -9 模拟更可控
真关电源不仅风险高、难复现,还会污染硬件环境。生产库常态化演练必须用软件方式模拟异常终止——核心就是向数据库主进程发送 SIGKILL(即 kill -9),绕过正常关闭流程,强制中断所有连接、丢弃未刷盘的 WAL 和 buffer 数据,触发 crash recovery 流程。
关键点在于:必须精准 kill 主进程(不是 pg_ctl 或 wrapper 进程),且确保数据库已启用 fsync=on 和 synchronous_commit=on(否则 recovery 行为不典型)。
- 查主进程 PID:
ps -eo pid,comm,args | grep postgres | grep -v "grep" | grep -v "pg_ctl",取第一个非辅助进程的pid - 避免误杀:用
pg_controldata先确认数据目录状态,再执行kill -9 $PID - PostgreSQL 12+ 可配合
pg_ctl stop -m immediate,但该命令仍会做 checkpoint,不如kill -9彻底;MySQL 则需 killmysqld主线程(ps -eLo pid,tid,comm,args | grep mysqld | grep -v "grep"找 TID=PID 的那行)
脚本必须校验 recovery 是否真正完成,不能只看进程起来
很多脚本用 pg_isready -U $USER -d $DB 或 mysqladmin ping 就判定“恢复成功”,这是严重误区。进程起来了,但可能卡在 replay WAL 阶段,查询仍报 database is starting up 或连接被拒绝。
PostgreSQL 要等 pg_is_in_recovery() 返回 false;MySQL 要确认 SHOW STATUS LIKE 'wsrep_local_state_comment'(Galera)或 SELECT @@innodb_buffer_pool_load_status(InnoDB warmup 完成)。
- PostgreSQL 示例轮询逻辑:
while pg_isready -q && psql -tAc "SELECT NOT pg_is_in_recovery();" 2>/dev/null | grep -q "t"; do sleep 2; done
- MySQL 示例(单机):
while ! mysql -e "SELECT 1" 2>/dev/null | grep -q "1"; do sleep 1; done && mysql -e "SELECT @@innodb_buffer_pool_load_status;" 2>/dev/null | grep -q "DONE"
- 超时必须设限(如 300 秒),超时则标记失败并保留
pg_log/或error.log快照
演练日志和指标要自动采集,否则无法闭环
一次断电后 recovery 耗时 8 秒还是 80 秒?WAL 重放了多少条?buffer pool 命中率是否跌穿阈值?这些不记录,演练就只是“跑通了”,没有持续改进价值。
建议在 kill 前后各采集一次基础指标快照,而非依赖事后人工查:
- PostgreSQL:
pg_stat_database(xact_rollback、blks_read)、pg_stat_bgwriter(checkpoints_timed、buffers_checkpoint) - MySQL:
SHOW GLOBAL STATUS(Innodb_buffer_pool_read_requests、Innodb_buffer_pool_reads)、SHOW ENGINE INNODB STATUS中的 LOG section - 统一用
date +%s.%3N打时间戳,输出到带日期前缀的/var/log/db-drill-$(date +%Y%m%d).log
定时任务 + 环境隔离是常态化的硬性前提
把脚本丢进 crontab 不等于常态化——没隔离的测试会污染开发/测试库,而 cron 缺少失败告警和历史追溯能力。
- 必须使用独立数据库实例(Docker 容器或专用 VM),数据目录与生产完全分离
- 用
systemd timer替代 cron:支持 OnFailure=、Persistent=true、随机延迟(RandomizedDelaySec=300),更适合故障演练节奏 - 每次执行前检查磁盘剩余空间(
df -B1 $PGDATA | awk 'NR==2 {print $4}'),低于 2GB 自动跳过并告警
最易被忽略的是:不同版本 PostgreSQL 对 pg_wal/archive_status/ 文件清理策略不同,MySQL 8.0.23+ 的 innodb_redo_log_capacity 会影响 recovery 启动速度——这些参数差异不纳入脚本判断逻辑,演练结果就不可比。











