从 Sentinel 事件流看故障转移投票:quorum 2、3 个 sentinel 时两个 sentinel 同时 +try-failover(epoch 1),各投自己一票,第三个 sentinel 投给先到的 sentinel0,sentinel0 以 2/3 当选(+elected-leader);quorum 1 但只剩 1 个 sentinel 可用时,o_down 立刻成立却拿不到多数票,12.1 s 后 -failover-abort-not-elected,25 秒都没有新主库。
订阅 Sentinel 的事件流,完整还原一次自动故障转移:kill -9 主库后 3046 ms s_down、3177 ms o_down、3256 ms 当选领导者、4200 ms 从库升主、4254 ms 切换主库、5280 ms 结束;写客户端不可用约 4.3 s;旧主库重启 10.2 s 后变成从库。
把本系列里 Sentinel 与 Redis Cluster 的实测数字放在一起:kill -9 主库后写入恢复 Cluster 约 5.4 s、Sentinel 约 4.3 s(同为 3000 ms 超时);Cluster 能在线扩容(4096 个槽 3.0 s),Sentinel 不能;多 key 命令在 Cluster 里受 CROSSSLOT 限制;两者都是异步复制,都有写入丢失的窗口。
用可切断的代理制造 Sentinel 与主库之间的分区:4.3 s 后 sentinel 完成故障转移,而不知情的客户端仍在向旧主库写入——两个主库同时接受写入;旧主库在恢复后 10.3 s 才被降级为从库;客户端收到 OK 的 1540 次写入里最终只保留了 314 条,丢失 1226 条。
对比三种客户端主库发现策略(kill -9 主库,sentinel 4.3 s 完成切换):固定地址 131 次失败、永不恢复;失败后重新问 sentinel 4294 ms 恢复;订阅 +switch-master 5407 ms 恢复。
把故障转移里的写入丢失窗口在三类故障里量出来:kill -9 主库,187377 条同步写与 3157700 条管道写共 8 次实验丢失 0 条;冻结再解冻的旧主库在解冻瞬间仍接受写入(3/3 次丢 1 条);sentinel 与主库分区、客户端继续写旧主库时,1540 条被回复 OK 的写入丢了 1226 条;加 min-replicas 后丢失降到 63 条。
实测 min-replicas-to-write:一主两从、max-lag 3 时冻结全部从库,主库在 3403 ms 后开始拒绝写入(NOREPLICAS Not enough good replicas to write.),此前接受 242 条;只冻结 1 个从库不拒绝;to-write 2 时冻结 1 个从库即停写(3421 ms);解冻后约 456 ms 恢复。
min-replicas-max-lag 的 lag 就是 INFO 里从库距上次 ACK 的秒数;在第 91 篇的分区实验里给旧主库加 min-replicas-to-write 1 / max-lag 3:客户端收到 OK 的写入从 1540 条降到 376 条,丢失从 1226 条降到 63 条,拒绝写入 1164 次(NOREPLICAS)。
实测 SLOWLOG:阈值 10 ms 时只记录超过阈值的命令(KEYS 21 ms、DEBUG SLEEP 50 ms);slowlog-max-len 5 是环形队列;阈值 0 记录一切、-1 关闭;参数最多记 32 个、每个 128 字节;被 DEBUG SLEEP 阻塞时另一个连接的 PING 感受到 251 ms,慢日志里却只有那条 DEBUG。
用 latency-monitor-threshold 5 ms 在开了 appendfsync always 的实例上制造慢命令与 fsync 延迟,LATENCY DOCTOR 输出了两类事件(command 与 aof-fsync-always)和对应的建议(查慢日志、放宽 fsync 策略、大对象删除的提示);没超过阈值的 fork 不会出现;阈值调到 100 ms 时 50 ms 的阻塞不被记录。
逐个命令看 Redis 的延迟监控:LATENCY LATEST 每个事件一行(事件名、最近一次时间戳与毫秒数、历史最大),HISTORY 每秒一个采样点(同一秒的 DEBUG SLEEP ×3 与 KEYS 合并为一个 100 ms 的采样点),GRAPH 画 ASCII 图,RESET 返回清除的事件数;阈值决定记不记录。
用已知次数的命令验证 INFO commandstats:calls 与执行次数完全一致(SET 1000、GET 3000……);usec_per_call 只含命令执行(GET 0.20 µs,而客户端往返 24.5 µs);failed_calls 计入执行失败(WRONGTYPE),rejected_calls 计入参数个数错误等未执行的命令;INFO latencystats 给出每个命令的分位数。