如何测试数据持久性(在各种故障后数据不丢失)?
测试数据持久性(即确保各种故障后已提交数据不丢失、未提交数据不被错误持久化)需要模拟真实场景中的各类故障,并验证故障恢复后的数据状态。核心思路是:在数据写入/提交的不同阶段注入故障,恢复后检查数据是否符合预期(已提交的完整保留,未提交的彻底回滚)。
一、核心测试场景设计(按故障类型划分)
1. 硬件级故障测试
(1)服务器突然断电/强制掉电
-
测试步骤:
- 准备测试数据:执行一批事务(如1000条INSERT/UPDATE),确保部分事务已提交(
COMMIT
),部分处于未提交状态(如持有锁但未执行COMMIT
)。 - 在事务提交后1秒内(或未提交事务执行中),通过物理断电、电源开关强制关闭服务器(模拟突发断电)。
- 重启服务器和数据库,检查:
- 已提交的事务数据是否完整存在;
- 未提交的事务是否完全回滚(无脏写、无部分提交);
- 数据库元数据(如索引、约束)是否完整(无损坏)。
- 准备测试数据:执行一批事务(如1000条INSERT/UPDATE),确保部分事务已提交(
-
关键验证点:数据库的持久化机制(如WAL预写日志)是否生效——断电前已写入日志的数据能否正确恢复。
(2)存储介质故障(如硬盘损坏、分区损坏)
-
测试步骤:
- 写入并提交一批数据(如10GB业务数据),确保数据已同步到磁盘(可执行
fsync
强制刷新缓存)。 - 模拟硬盘故障:通过工具(如
dd if=/dev/zero of=/path/to/db/disk
破坏数据库数据分区,或拔掉物理硬盘)。 - 尝试通过备份恢复(如冷备份、增量备份)或副本(如主从架构的从库)恢复数据,检查:
- 恢复后的数据是否与故障前已提交的数据完全一致;
- 恢复过程是否能识别损坏的存储块并通过冗余数据修复。
- 写入并提交一批数据(如10GB业务数据),确保数据已同步到磁盘(可执行
-
关键验证点:备份/副本机制是否能覆盖所有已提交数据,存储冗余(如RAID)是否有效。
2. 软件级故障测试
(1)数据库进程崩溃(kill -9强制终止)
-
测试步骤:
- 执行事务:例如,一个包含多表更新的长事务(如先更新订单表,再更新库存表),在
COMMIT
执行瞬间或未提交时,通过kill -9 <db_pid>
强制终止数据库进程。 - 重启数据库,检查:
- 若事务已完成
COMMIT
,数据是否完整写入; - 若事务未提交(或
COMMIT
未完成),是否回滚到初始状态(无中间状态数据); - 数据库日志(如MySQL的binlog、PostgreSQL的pg_log)是否记录故障及恢复过程,无日志损坏。
- 若事务已完成
- 执行事务:例如,一个包含多表更新的长事务(如先更新订单表,再更新库存表),在
-
关键验证点:进程崩溃时,数据库是否能通过事务日志(如undo/redo log)正确恢复一致性。
(2)操作系统崩溃(如内核panic)
- 测试步骤:
- 批量写入数据并提交(如通过脚本循环执行
INSERT + COMMIT
)。 - 在数据写入高峰期,通过工具(如
sysrq-trigger
触发内核panic:echo c > /proc/sysrq-trigger
)强制OS崩溃。 - 重启操作系统和数据库,检查:
- 崩溃前最后一个已提交事务的数据是否完整;
- 数据库是否自动执行崩溃恢复(如InnoDB的crash recovery),且恢复后无数据丢失或损坏。
- 批量写入数据并提交(如通过脚本循环执行
3. 网络/集群故障测试(分布式数据库场景)
(1)主从同步中断时主库故障
-
测试步骤(以主从架构为例):
- 主库执行事务并提交(假设配置为“异步复制”),此时数据尚未同步到从库(可通过延迟复制参数
slave_delay
控制)。 - 在主库提交后、数据同步到从库前,强制关闭主库(如
kill -9
)。 - 将从库提升为主库,检查:
- 主库已提交但未同步的事务数据是否丢失(异步复制允许丢失,同步复制需完全保留,需符合预期);
- 提升后的从库数据是否一致(无重复或缺失)。
- 主库执行事务并提交(假设配置为“异步复制”),此时数据尚未同步到从库(可通过延迟复制参数
-
关键验证点:复制机制的持久性配置(同步/异步)是否符合设计预期,数据在集群节点间的一致性。
(2)分布式事务中间态故障(如两阶段提交中断)
- 测试步骤(针对分布式数据库或跨库事务):
- 执行跨节点分布式事务(如TCC或两阶段提交),在“prepare”阶段完成后、“commit”指令发送前,中断协调者节点(如断电)。
- 恢复协调者和参与节点,检查:
- 事务是否最终一致(要么全提交,要么全回滚);
- 参与节点是否能通过日志(如事务状态日志)识别未完成事务并自动修复。
二、关键工具与技术手段
-
故障注入工具:
- 硬件级:电源控制器(如IPMI强制断电)、存储模拟器(如
dm-flakey
模拟磁盘临时故障)。 - 软件级:
kill
/pkill
(终止进程)、sysrq
(触发OS崩溃)、tc
(网络中断)。 - 数据库内置工具:如PostgreSQL的
pg_ctl stop -m immediate
(强制终止,不做清理)、MySQL的innodb_force_recovery
(模拟崩溃后恢复)。
- 硬件级:电源控制器(如IPMI强制断电)、存储模拟器(如
-
数据验证工具:
- 校验和对比:对故障前后的表数据计算MD5/CRC校验和(如
pg_checksums
),确认一致性。 - 逻辑备份对比:故障前后分别执行
mysqldump
/pg_dump
,通过diff
工具对比备份文件。 - 业务校验:通过业务API查询关键数据(如订单总数、余额总和),验证业务逻辑一致性。
- 校验和对比:对故障前后的表数据计算MD5/CRC校验和(如
三、核心验证指标
- 数据完整性:已提交数据100%保留,无丢失或篡改。
- 事务一致性:未提交事务100%回滚,无脏数据、中间态数据。
- 恢复有效性:故障后无需人工干预,数据库能自动恢复(或通过备份/副本恢复后数据完整)。
- 性能影响:持久化机制(如WAL刷盘)对正常写入性能的影响是否在预期范围内(避免为了极致持久化导致性能暴跌)。
总结
数据持久性测试的核心是“覆盖全链路故障”——从数据写入内存、到日志落盘、再到存储持久化的每个环节,都需模拟故障并验证恢复结果。最终目标是:任何故障都不应导致已提交数据丢失,也不应让未提交数据错误生效,同时确保恢复过程可靠、高效。