一、测试设计核心思路
- 模拟真实场景
- 区分计划内切换(运维手动切换)和故障切换(主库宕机、网络分区)。
- 覆盖不同负载:空闲状态、高并发读写、大事务执行中切换。
- 数据可追踪
- 生成可验证的唯一数据(如全局ID、哈希值),确保每条数据可溯源。
- 验证维度全面
- 数据完整性:数据是否丢失
- 数据正确性:主从数据是否完全一致
- 复制延迟:切换后新主库是否包含旧主库崩溃前的最后事务
- 业务连续性:应用能否无感知重连新主库
二、具体测试用例设计
场景1:基础数据一致性验证
测试步骤 |
预期结果 |
1. 主库插入1000条带唯一ID的数据(如INSERT ... VALUES (uuid, ...) ) |
数据实时同步到从库 |
2. 触发切换(手动或模拟主库宕机) |
从库提升为新主库,切换时间 ≤ RTO(如5秒) |
3. 应用重连新主库,查询所有记录的ID |
返回1000条记录,无丢失 |
4. 用pt-table-checksum 对比新旧主库数据 |
校验结果一致(DIFF=0) |
场景2:高并发写入时切换
测试步骤 |
预期结果 |
1. 启动多线程压测工具(如sysbench),持续写入数据 |
主从复制延迟保持正常(Seconds_Behind_Master < 1s) |
2. 强制杀死主库进程 |
从库自动提升为主库 |
3. 检查压测日志:记录最后一次成功写入的ID(如MAX(id) ) |
新主库应包含该ID |
4. 校验数据:SELECT COUNT(*) + pt-table-checksum |
数据总量一致,无差异 |
场景3:事务中的切换(严苛场景)
测试步骤 |
预期结果 |
1. 主库执行大事务:
BEGIN;
INSERT 1000行;
UPDATE 1000行;
COMMIT; |
事务未提交时切换,新主库不应出现部分数据 |
2. 在COMMIT 前触发主库宕机 |
事务自动回滚,新主库中无该事务的脏数据 |
3. 检查binlog位置(SHOW MASTER STATUS ) |
新主库的binlog position ≥ 旧主库事务开始位置 |
场景4:网络分区(脑裂场景)
测试步骤 |
预期结果 |
1. 模拟主库网络隔离(iptables阻断流量) |
集群自动检测故障,触发从库切换 |
2. 旧主库恢复网络后尝试写入数据 |
写入失败(因旧主库已被降级为从库,且read_only=ON ) |
3. 人工介入恢复旧主库:重新配置复制指向新主库 |
旧主库从新主库同步数据,最终数据一致 |
三、关键工具与技术选型
- 数据生成与压测
sysbench
:模拟OLTP读写负载
- 自定义脚本:插入带哈希值的数据(如
MD5(CONCAT(id, data))
)
- 数据一致性校验
- Percona Toolkit:
pt-table-checksum
(低锁表风险)
- MySQL内置校验:
CHECKSUM TABLE
(适用于小表)
- 故障注入
- Chaos Engineering工具:
Chaos Mesh
(K8s环境):模拟Pod故障、网络延迟
kill -9
:强制终止MySQL进程
- 复制状态监控
SHOW SLAVE STATUS
:检查Seconds_Behind_Master
, Last_IO_Error
- Prometheus +
mysqld_exporter
:实时监控复制延迟
四、自动化实现方案(Python示例)
import pymysql
import subprocess
def test_failover_consistency():
# 1. 初始化数据
master_conn = pymysql.connect(host="master", user="test")
with master_conn.cursor() as cursor:
cursor.execute("INSERT INTO test.data (id, hash) VALUES (1, MD5('data1'))")
master_conn.commit()
# 2. 触发故障切换(通过Chaos Mesh API或kubectl)
subprocess.run("chaosd attack network delay --latency 500ms --duration 10m", shell=True)
# 3. 等待切换完成(监控新主库选举)
new_master = wait_for_new_master() # 实现集群状态检测
# 4. 验证数据
new_conn = pymysql.connect(host=new_master, user="test")
with new_conn.cursor() as cursor:
cursor.execute("SELECT hash FROM test.data WHERE id=1")
assert cursor.fetchone()[0] == "a1b2c3d4..." # 预期哈希值
# 5. 校验全局一致性
subprocess.run("pt-table-checksum --host={new_master} --databases test", shell=True)
# 解析pt-table-checksum输出,确认DIFF=0
def wait_for_new_master(timeout=30):
# 实现逻辑:轮询集群状态API或查询MySQL元数据库(如MGR的`performance_schema.replication_group_members`)
...
五、注意事项
- GTID优先:确保开启GTID(
gtid_mode=ON
),避免binlog position切换后数据错位。
- 半同步复制:配置
rpl_semi_sync_master_wait_point=AFTER_SYNC
,确保事务提交前至少同步到一个从库。
- 脑裂防护:使用中间件(ProxySQL)或数据库代理(如MySQL Router)自动屏蔽旧主库写入。
- 数据验证时机:切换后等待
1-2倍复制延迟时间
再校验,避免误报。
面试回答点睛:
强调设计中的破坏性测试思维(如主动注入事务中断、网络分区)和自动化闭环能力(从故障注入到结果校验全流程自动化)。可补充:“我会在K8s环境中用Chaos Mesh模拟生产级故障,并通过校验服务化接口将结果集成到自动化测试平台”。