【Redis从入门到精通】第45篇:主观下线和客观下线——Sentinel怎么判断主库挂了

【Redis从入门到精通】第45篇:主观下线和客观下线——Sentinel怎么判断主库挂了 上一篇【第44篇】Sentinel启动与监控——它是怎么盯着主服务器的下一篇【第46篇】Sentinel选主——Raft算法的精妙应用判断一个服务器是不是挂了看似简单实则暗藏玄机。网络抖动、瞬间高负载、TCP重传都可能导致响应超时。如果仅凭一个哨兵的一面之词就说主库挂了那误判率恐怕比你的代码bug率还高。所以Sentinel设计了两级下线判断机制主观下线SDOWN和客观下线ODOWN。一、主观下线SDOWN一家之言当Sentinel向被监控的实例发送PING命令时如果实例在down-after-milliseconds时间内没有回复有效响应该Sentinel就会将其标记为主观下线Subjectively DownSDOWN。Sentinel Master │ │ │ ─── PING ────────────────→ │ │ ←── PONG正常 │ │ │ │ ─── PING ────────────────→ │ │ 等待... │ │ 等待... │ │ 超过 down-after-milliseconds │ │ │ │ ✗ 标记为主观下线SDOWN │有效回复包括PONG、-LOADING、-MASTERDOWN无效回复包括无回复、其他错误回复# sentinel.conf 中配置sentinel down-after-milliseconds mymaster30000# 含义如果30秒内主库没回复有效PING标记为SDOWN关键点SDOWN是单个Sentinel的判断不代表主库真的挂了从库和Sentinel也可以被标记为SDOWN不同Sentinel的down-after-milliseconds可以不同但通常配成一样二、客观下线ODOWN多数共识当一个Sentinel认为主库SDOWN后它会向其他Sentinel询问“你也觉得主库挂了吗”。如果超过quorum个Sentinel都认为主库SDOWN主库就被标记为客观下线Objectively DownODOWN。┌───────────────────────────────────────────────────┐ │ 主观下线 vs 客观下线 │ ├───────────────────┬─────────────────────────────────┤ │ 主观下线 SDOWN │ 客观下线 ODOWN │ │ │ │ │ 单个Sentinel判断 │ 超过quorum个Sentinel同意 │ │ 可以是任何实例 │ 只针对主服务器 │ │ 不触发故障转移 │ 触发故障转移 │ │ 可能是误判 │ 误判概率极低 │ └───────────────────┴─────────────────────────────────┘重要客观下线只针对主服务器。从服务器和Sentinel只有主观下线没有客观下线。因为只有主库挂了才需要故障转移从库挂了只需要标记一下就行。三、从SDOWN到ODOWN的完整流程Sentinel 1 Sentinel 2 Sentinel 3 Master │ │ │ │ │ PING无回复 │ │ │ │ ──────────────────────────────────────────→ │ │ │ │ │ │ ① 标记SDOWN │ │ │ │ │ │ │ │ ② 询问其他Sentinel │ │ │ ──── is-master-down-by-addr ──→│ │ │ ──── is-master-down-by-addr ──────────────→ │ │ │ │ │ │ ←── Yes ──────│←── Yes ──────│ │ │ │ │ │ │ ③ 统计票数3个Sentinel中3个认为下线 │ │ 3 ≥ quorum(2) → ODOWN │ │ │ │ │ │ ④ 发起故障转移 │ │ │更详细的时序图时间轴 → S1: PING─→(超时)─→SDOWN─→询问S2,S3──→收到2个Yes──→ODOWN──→故障转移 S2: PING─→(超时)─→SDOWN←─询问───→回复Yes S3: PING─→(超时)─→SDOWN←─询问────────────→回复Yes │←─ down-after ─→│←── 投票 ──→│←─ 执行 ─→│ │ milliseconds │ │ failover │四、SENTINEL is-master-down-by-addr命令这个命令是Sentinel之间投票的核心# 语法SENTINEL is-master-down-by-addripportcurrent_epochrunid# 示例Sentinel 1 询问 Sentinel 2SENTINEL is-master-down-by-addr192.168.1.10063791s1_runid_xxx# Sentinel 2 的回复1)(integer)1# 1认为下线, 0认为没下线2)s2_runid_yyy# Sentinel 2 的运行ID3)(integer)1# Sentinel 2 的当前纪元参数说明ip port被怀疑下线的主库地址current_epoch发起投票的Sentinel的当前纪元runid如果是Sentinel选举传自己的runid如果是下线检测传*注意这个命令有双重用途——既用于判断ODOWN也用于Sentinel的leader选举下一篇详述。五、quorum配置的意义quorum是sentinel.conf中的关键参数sentinel monitor mymaster127.0.0.163792# ↑ quorum2quorum与故障转移的关系┌──────────────────────────────────────────────────┐ │ quorum 的两个作用 │ ├──────────────────────────────────────────────────┤ │ │ │ 1. 判断ODOWN≥quorum个Sentinel认为SDOWN │ │ → 触发故障转移的前提条件 │ │ │ │ 2. 不是执行条件 │ │ 故障转移的执行需要 半数Sentinel投票选leader │ │ 例3个Sentinel需要2个以上投票 │ │ 5个Sentinel需要3个以上投票 │ │ │ │ 所以即使quorum1也需要半数Sentinel才能执行 │ └──────────────────────────────────────────────────┘典型配置示例Sentinel数量quorum含义323个中2个认为下线→ODOWN需1即2票选leader535个中3个认为下线→ODOWN需2即3票选leader525个中2个认为下线→ODOWN更敏感但执行仍需3票踩坑提示quorum设为1是危险的如果只有1个Sentinel认为主库下线就能触发ODOWN一次网络抖动就可能导致误切换。生产环境quorum至少设为2。六、为什么需要客观下线如果只有一个Sentinel网络分区可能造成这样的惨剧没有ODOWN的灾难场景 ┌────────────────────────────────┐ │ 网络分区 │ │ │ │ ┌─────┐ ┌─────────┐ │ │ │ S1 │ │ Master │ │ │ │ │ ✗ │ 正常 │ │ │ └─────┘ └─────────┘ │ │ (S1认为Master挂了) │ │ │ │ ┌─────┐ ┌─────────┐ │ │ │ S2 │ ✓ │ Slave │ │ │ │ S3 │ ✓ │ 正常 │ │ │ └─────┘ └─────────┘ │ │ (S2、S3知道Master正常) │ └────────────────────────────────┘ 如果只有S1说了算 → 误切换 有了ODOWN → S2、S3不同意 → 不切换客观下线机制确保只有多数Sentinel都同意才认为主库真的挂了。这就像法庭判案一个人说了不算得有陪审团多数同意才行。七、PING超时检测的实现细节Sentinel的PING检测并非简单的发送-等待-超时模式而是基于异步事件循环的实现Sentinel 每秒执行一次 sentinelTimer(): 1. 向所有实例发送 PING 2. 检查所有实例的 last_pong_time 3. 如果 now - last_pong_time down_after_period: - 实例未标记SDOWN → 标记SDOWN - 如果是主库 → 向其他Sentinel询问 4. 如果收到有效回复: - 更新 last_pong_time - 如果实例标记了SDOWN → 取消SDOWN主库恢复后如何自动恢复监控当主库从SDOWN/ODOWN恢复后1. 主库回复PING → Sentinel取消SDOWN标记 2. Sentinel询问其他Sentinel → 票数不足 → 取消ODOWN 3. 如果故障转移已在进行中 → 不会中断已经开始了就得走完 4. 如果故障转移尚未开始 → 取消故障转移踩坑提示如果主库在故障转移过程中恢复了故障转移不会回滚。新主库已经晋升老主库恢复后会被设置为从库复制新主库。八、实战演示观察Sentinel的切换日志让我们模拟一次主库宕机观察Sentinel的行为# 步骤1查看当前状态$ redis-cli-p26379SENTINEL master mymaster1)name2)mymaster3)ip4)192.168.1.1005)port6)63797)flags8)master# 当前状态正常...# 步骤2强制停止主库$ redis-cli-p6379DEBUG SLEEP60# 或直接 kill 掉主库进程# 步骤3观察Sentinel日志30秒后[12345]26Dec10:00:30.000# sdown master mymaster 192.168.1.100 6379# ↑ Sentinel 1 标记主观下线[12345]26Dec10:00:31.000# odown master mymaster 192.168.1.100 6379 #quorum 2/2# ↑ 收到2个Sentinel同意标记客观下线[12345]26Dec10:00:31.500# new-epoch 1# ↑ 进入新的纪元[12345]26Dec10:00:31.500# try-failover master mymaster 192.168.1.100 6379# ↑ 开始尝试故障转移[12345]26Dec10:00:32.000# vote-for-leader abc123 1# ↑ 投票给某个Sentinel作为leader[12345]26Dec10:00:33.000# selected-slave slave 192.168.1.102:6381# ↑ 选中从库作为新主库[12345]26Dec10:00:33.000# failover-state-send-slaveof-noone slave 192.168.1.102:6381# ↑ 发送REPLICAOF NO ONE命令[12345]26Dec10:00:34.000# failover-state-wait-promotion slave 192.168.1.102:6381# ↑ 等待从库晋升完成[12345]26Dec10:00:35.000# promoted-slave slave 192.168.1.102:6381# ↑ 从库晋升成功[12345]26Dec10:00:35.000# failover-state-reconf-slaves master mymaster 192.168.1.100 6379# ↑ 重新配置其他从库[12345]26Dec10:00:36.000# slave-reconf-sent slave 192.168.1.101:6380# ↑ 已发送重新配置命令给从库[12345]26Dec10:00:38.000# failover-end master mymaster 192.168.1.100 6379# ↑ 故障转移完成[12345]26Dec10:00:38.000# switch-master mymaster 192.168.1.100 6379 192.168.1.102 6381# ↑ 主库已切换Sentinel日志中的关键事件标记事件含义sdown主观下线-sdown取消主观下线odown客观下线-odown取消客观下线try-failover开始尝试故障转移selected-slave选中从库晋升failover-end故障转移完成switch-master主库切换完成总结SDOWN和ODOWN是Sentinel判断主库是否故障的两道关卡。SDOWN是单点检测灵敏但可能误判ODOWN是多节点共识稳健但需要时间。quorum参数决定了ODOWN的触发门槛通常设为Sentinel数量的过半数。理解了这套判断机制你就能明白为什么Sentinel有时候反应慢在等quorum为什么有时候不作为票数不够。下一篇我们将深入Sentinel选主的核心算法——Raft协议的精妙应用看看故障转移时如何选出领头的Sentinel和最优的从库。上一篇【第44篇】Sentinel启动与监控——它是怎么盯着主服务器的下一篇【第46篇】Sentinel选主——Raft算法的精妙应用