模拟 Redis 节点宕机,如何验证缓存集群在 30 秒内完成故障转移

解读

面试官真正想考察的是“性能测试工程师能否把‘高可用’这一非功能需求,转化为可量化、可复现、可自动化的测试方案”。
在国内一线/二线互联网公司,Redis 99.9% 以哨兵或 Cluster 模式部署在 Kubernetes 或物理机混合云环境,SLA 普遍要求“故障转移 ≤30 s、数据丢失 ≤1 s、客户端影响 ≤5%”。
因此,回答必须同时覆盖:

  1. 如何“制造”真实宕机而非优雅关闭;
  2. 如何“度量”30 s 内完成切换(含主观主客观指标);
  3. 如何“加压”保证结论在生产并发下成立;
  4. 如何“输出”一份开发、运维、测试三方都能落地的报告。

知识点

  1. Redis 高可用原理:哨兵(sentinel)选主、Cluster 槽位迁移、主观下线(sdown)与客观下线(odown)、leader sentinel 选举、配置传播。
  2. 故障模拟手段:
    • 网络隔离:tc/netem、iptables、Calico NetworkPolicy;
    • 进程级:kill -9、docker pause、sysrq-trigger;
    • 宿主机级:宕机、断网、kubelet 停止。
  3. 观测指标:
    • 客观指标:failover-start(+switch-master 消息)、failover-end(+promoted 消息)、client 报错窗口、QPS 跌落与恢复、RT 99 线抖动;
    • 主观指标:业务黄金链路成功率、用户态错误日志。
  4. 性能测试工具:
    • 负载层:redis-benchmark、memtier、go-ycsb、JMeter Redis sampler;
    • 观测层:redis_exporter + Prometheus、ELK、Grafana、Loki、SkyWalking;
    • 自动化:Python pytest + Allure + Jenkins/ GitLab CI。
  5. 国内合规:等保 2.0 要求“测试活动不得携带生产数据”,故需构造等效脱敏数据集;云上需提前提交“混沌演练”工单,避免安全告警。

答案

一、测试设计

  1. 环境对齐

    • 1:1 复制生产规格:同 CPU 型号、同 NUMA、同内核参数(vm.swappiness=1、transparent_hugepage=never)。
    • 数据量:RDB 文件大小 ≥生产 80%,key 数量 5000 万,大 key(≥10 KB)占比 5%,热 key(访问频率 top 1%)占比 0.1%。
    • 负载模型:使用 memtier 模拟 8 万并发连接,读写比 1:4,value 平均 2 KB,QPS 压到生产峰值 120%,持续 30 min 作为基线。
  2. 故障注入策略
    采用“网络分区+进程杀”双保险,确保触发最严苛的“双节点不可达”场景:
    a) 选取当前主节点 M1,通过 Ansible 批量下发
    iptables -A INPUT -p tcp --dport 6379 -j DROP && iptables -A OUTPUT -p tcp --sport 6379 -j DROP
    制造网络隔离;
    b) 同时向 M1 发送 kill -9 <redis-pid>,防止其恢复后形成脑裂;
    c) 注入后,立即在哨兵端监听 +switch-master 事件,以该事件时间戳为 T0。

  3. 指标采集

    • 时间维度:T0、T1(第一个从节点晋升成功)、T2(客户端报错率<1%)、T3(99 RT 恢复至基线 110% 以内)。
    • 数据维度:每秒采集哨兵日志、Redis info replication、客户端错误计数、业务黄金接口成功率。
    • 资源维度:CPU steal、上下文切换、网络重传、TCP retrans。
  4. 判定标准
    若 T1-T0 ≤30 s 且 T2-T0 ≤35 s 且 T3-T0 ≤60 s,则判定通过;否则记为高风险,需输出火焰图与线程栈,定位是否因 cluster-node-timeout、sentinel down-after-milliseconds 配置过大或客户端重连退避导致。

  5. 自动化与回归
    用 Python 封装 ChaosBlade 企业版 API,在 Jenkins nightly pipeline 中定时触发;测试报告自动推送到飞书群,并创建 Jira 缺陷单指派给 DBA 与 SRE。

二、示例脚本(核心片段)

# redis_failover_validate.py
import redis, time, subprocess, prometheus_client

def inject_failover(master_ip):
    subprocess.run(f"ansible redis -i {master_ip}, -m shell -a 'iptables -A INPUT -p tcp --dport 6379 -j DROP'", shell=True)
    subprocess.run(f"ssh {master_ip} 'killall -9 redis-server'", shell=True)

def wait_promotion(sentinel_host, master_name, timeout=30):
    r = redis.Redis(sentinel_host, port=26379, decode_responses=True)
    pubsub = r.pubsub()
    pubsub.subscribe("+switch-master")
    for msg in pubsub.listen():
        if msg and master_name in str(msg):
            return time.time()
    raise TimeoutError("failover not finish in 30s")

if __name__ == "__main__":
    t0 = inject_failover("10.0.0.11")
    t1 = wait_promotion("10.0.0.20", "mymaster")
    cost = t1 - t0
    print("failover duration:", cost)
    assert cost <= 30, "failover SLA breach"

三、交付物

  1. 《Redis 高可用故障转移性能测试报告》PDF,含趋势图、配置 diff、瓶颈分析;
  2. 经运维评审的 sentinel.conf / redis.conf 优化项(如 down-after-milliseconds 由 30 s 改为 15 s,parallel-syncs 由 1 改为 2);
  3. 自动化用例入库 TestOne 平台,标签“P0-高可用-Redis”,随版本强制回归。

拓展思考

  1. 多 AZ 场景:国内主流云厂商跨可用区 RT 2~3 ms,若哨兵 quorum 设置为 3,但 AZ 级故障导致 2 个哨兵失联,可能无法形成 majority。测试时需把“AZ 掉电”纳入故障域,验证是否出现“双主”或“拒绝写入”。
  2. 客户端重连策略:Jedis 与 Lettuce 默认退避算法不同,Lettuce 支持异步重连,故障窗口更短。性能测试应分别压测两种 SDK,给出选型建议。
  3. 数据一致性:当主节点宕机瞬间仍有 1 ms 数据未同步,金融账务场景无法容忍。可引入 WAIT 命令或 Redis Raft(国内阿里云 Tair、腾讯云 CRS 已支持)做强同步验证,测试脚本需对比 failover 后 RPO 是否=0。
  4. 大规模 Cluster:若槽位 16384 个、节点 200+,故障切换涉及多槽位迁移,30 s 内完成需保证 cluster-migration-barrier 与 migrate-timeout 调优。性能测试需模拟 200 节点全负载,观察“槽位迁移带宽”是否成为新瓶颈。