Redis集群发生故障后到故障转移机制测试
Redis Cluster 是 Redis 的分布式解决方案,通过分片(sharding)将数据分散到多个节点,并利用主从复制和自动故障转移保证高可用性。当节点故障时,其读写流程会依赖集群的故障检测、自动故障转移机制动态调整,确保服务连续性。以下是具体流程及测试验证方法:
一、Redis Cluster 节点故障时的读写流程
Redis Cluster 中每个分片(slot)由一个主节点(master)负责读写,主节点有1个或多个从节点(slave)作为备份。节点故障分为主节点故障和从节点故障,处理逻辑不同:
1. 故障检测:如何发现节点不可用?
集群中所有节点通过Gossip协议定期交换状态(默认每1秒发送PING消息,包含自身状态和已知节点的状态)。当节点A超过cluster-node-timeout
(默认15秒)未收到节点B的PONG响应时,A会标记B为“疑似下线(PFAIL)”。
若超过半数的主节点都标记某节点为PFAIL,则该节点被标记为“确定下线(FAIL)”,并通过Gossip协议广播给全集群,触发后续处理。
2. 从节点故障:对读写无直接影响
从节点仅作为主节点的备份,不处理读写请求(除非客户端显式指定从节点读)。若从节点故障:
- 主节点会检测到从节点下线,但不影响分片的读写(主节点仍正常服务);
- 集群会暂时失去该主节点的一个备份,若主节点后续故障,可能因无可用从节点导致分片不可用(需避免单从节点故障)。
3. 主节点故障:自动故障转移与读写切换
主节点故障是影响最大的场景,集群会自动触发故障转移(类似Redis Sentinel的逻辑),流程如下:
(1)从节点竞选新主节点
- 故障主节点的所有从节点会竞选新主节点:通过Raft-like算法,从节点向集群中其他主节点发送投票请求,获得多数票(超过半数主节点支持)的从节点晋升为新主节点。
(2)更新集群状态
- 新主节点会接管原主节点的所有slot,并通过Gossip协议广播“slot归属变更”;
- 其他节点更新本地的“slot-节点映射表”,旧主节点被标记为FAIL。
(3)读写请求的路由调整
-
故障转移前(主节点刚下线,未选出新主):
客户端向原主节点发送读写请求时,会收到“CLUSTERDOWN Hash slot not served”错误(此时分片不可用)。 -
故障转移中(新主节点正在竞选):
客户端可能收到“MOVED”或“ASK”重定向消息(取决于节点是否已更新slot映射),客户端会根据重定向更新本地路由表,重试请求。 -
故障转移后(新主节点接管slot):
客户端通过更新后的路由表,直接向新主节点发送读写请求(读请求也可路由到新主节点的从节点,若开启readonly
)。
4. 极端场景:无可用从节点的主节点故障
若故障主节点无从节点(或所有从节点也故障),则该主节点负责的slot会永久不可用,客户端访问这些slot时会持续收到“CLUSTERDOWN”错误,直到手动恢复节点。
二、测试Redis Cluster故障处理正确性的验证方法
验证需覆盖故障检测准确性、故障转移完整性、读写路由正确性和数据一致性,需在隔离环境中模拟各类故障场景。
1. 测试环境准备
- 部署Redis Cluster:至少3个主节点(每个主节点负责部分slot),每个主节点配置1个从节点(共6节点,满足多数投票条件);
- 客户端工具:
redis-cli
(原生客户端)、自定义脚本(模拟并发读写,如Python的redis-py-cluster
); - 监控工具:
redis-cli cluster nodes
(查看节点状态)、redis-cli cluster slots
(查看slot归属)、redis-cli info replication
(查看主从关系)。
2. 核心测试场景与验证步骤
场景1:主节点故障后的自动故障转移
测试目标:验证主节点故障后,从节点能否正确晋升为主节点,slot归属是否更新,读写是否恢复。
测试步骤:
-
初始状态确认:
- 记录集群状态:
redis-cli -h <主节点IP> cluster nodes
,确认主节点A(如192.168.1.1:6379)及其从节点A-slave(192.168.1.2:6379),以及A负责的slot范围(如0-5460)。 - 向A写入测试数据:
redis-cli -c -h <A的IP> set slot_key "test_value"
(-c
启用集群模式,自动处理重定向)。
- 记录集群状态:
-
模拟主节点故障:
- 强制终止主节点A的进程:
ps -ef | grep redis-server | grep 6379 | awk '{print $2}' | xargs kill -9
。
- 强制终止主节点A的进程:
-
观察故障转移过程:
- 每隔1秒执行
redis-cli cluster nodes
,观察A的状态变化(从“online”→“PFAIL”→“FAIL”),以及A-slave的状态(从“slave”→“master”)。 - 记录故障转移耗时(从主节点故障到新主节点上线的时间,通常≤30秒)。
- 每隔1秒执行
-
验证slot归属与读写:
- 执行
redis-cli cluster slots
,确认原A负责的slot(0-5460)已归属A-slave(新主节点)。 - 读取数据:
redis-cli -c -h <A-slave的IP> get slot_key
,验证返回“test_value”(数据未丢失)。 - 写入新数据:
redis-cli -c -h <A-slave的IP> set new_key "new_value"
,验证写入成功,且其他节点可通过重定向访问。
- 执行
预期结果:
- 主节点A被标记为FAIL,从节点A-slave晋升为新主,接管所有slot;
- 故障转移后,原slot的读写请求可正常路由到新主节点,数据完整。
场景2:故障转移期间的客户端读写行为
测试目标:验证客户端在故障转移期间能否正确处理错误和重定向,最终恢复正常读写。
测试步骤:
- 启动自定义客户端脚本:持续向主节点A的slot发送读写请求(每秒10次),记录请求结果(成功/失败/错误类型)。
- 模拟主节点A故障(同场景1步骤2),观察脚本日志:
- 故障初期:是否出现“CLUSTERDOWN Hash slot not served”错误(分片不可用);
- 故障转移中:是否收到“MOVED”重定向消息(客户端自动更新路由表);
- 故障转移后:请求是否恢复成功(无错误)。
预期结果:
- 故障转移期间有短暂(通常10-30秒)的读写失败;
- 客户端收到重定向后自动更新路由,故障转移完成后读写恢复正常,无数据丢失。
场景3:从节点故障对主节点的影响
测试目标:验证从节点故障后,主节点的读写是否不受影响,集群是否仍能正常服务。
测试步骤:
- 确认主节点B及其从节点B-slave正常运行,B负责slot 5461-10922。
- 向B写入数据:
set b_key "b_value"
,验证写入成功。 - 模拟B-slave故障:
kill -9 <B-slave的PID>
。 - 验证主节点B状态:
redis-cli -h <B的IP> info replication
,确认B仍为“master”,且“connected_slaves”变为0。 - 向B执行读写操作:
get b_key
(应返回“b_value”)、set b_new "new"
(应成功)。
预期结果:
- 从节点B-slave故障后,主节点B仍正常处理读写,集群无错误;
- 若后续B故障,因无可用从节点,其负责的slot会不可用(验证此场景可进一步杀死B,观察是否返回CLUSTERDOWN)。
场景4:网络分区导致的“脑裂”防护
测试目标:验证网络分区时,集群能否避免双主节点(脑裂),保证数据一致性。
测试步骤:
- 集群包含3主3从(主A、B、C;从A-slave、B-slave、C-slave),网络正常时A为某slot的主节点。
- 模拟网络分区:将主节点A与其他节点隔离(如
iptables
阻断A与B、C的通信),但A仍能接收客户端请求。 - 观察集群状态:
- 分区外节点(B、C、A-slave等)会标记A为FAIL,A-slave晋升为新主;
- 分区内的A因无法与多数节点通信,会降级为从节点(即使仍能接收请求,也会拒绝写入,返回“MOVED”)。
- 向分区内的A发送写入请求:
set conflict_key "a"
,验证是否被拒绝(避免双写)。 - 恢复网络后,观察A是否同步新主节点的数据,成为其从节点。
预期结果:
- 网络分区时,仅分区外的新主节点(A-slave)可处理写入,分区内的原主A被降级,拒绝写入(无脑裂);
- 网络恢复后,原主A同步新主数据,集群状态一致。
场景5:数据一致性验证(故障转移后)
测试目标:验证故障转移后,新主节点的数据与原主节点故障前的数据是否一致(无丢失/篡改)。
测试步骤:
- 向主节点A写入大量数据(10万条,包含字符串、哈希等类型),记录数据指纹(如所有key的MD5总和:
redis-cli --raw keys "*" | sort | xargs redis-cli mget | md5sum
)。 - 杀死主节点A,等待从节点A-slave晋升为新主。
- 在新主节点A-slave上计算相同的数据指纹,与故障前对比。
- 随机抽查10%的key,验证value内容、过期时间等是否一致。
预期结果:
- 新主节点的数据指纹与原主节点完全一致,随机抽查的key内容无差异(从节点实时同步主节点数据,故障转移不丢失数据)。
三、测试工具与自动化
- 故障注入:使用
kill -9
模拟进程崩溃,iptables
模拟网络分区,dd
填充磁盘模拟节点不可用; - 自动化脚本:用Python(
redis-py-cluster
)编写并发读写脚本,记录请求成功率、响应时间、错误类型; - 状态监控:定时执行
cluster nodes
和cluster slots
,解析输出判断节点状态和slot归属是否正确; - 报告生成:汇总故障转移耗时、数据一致性结果、客户端错误率等指标,生成测试报告。
总结
Redis Cluster 节点故障时的核心保障是自动故障转移:主节点故障后,从节点通过竞选晋升为新主,接管slot并恢复读写;从节点故障不影响主节点服务。测试时需重点验证:故障检测准确性、故障转移完整性(新主接管slot)、客户端路由正确性(重定向处理)、数据一致性(无丢失)及极端场景(网络分区、无从节点)的容错能力。通过工具自动化和场景覆盖,可确保集群在故障时的高可用性。