目录
[1. 详细步骤分解](#1. 详细步骤分解)
[2. 总结与关键点](#2. 总结与关键点)
[二、"引导连接" 和 "常规操作"](#二、“引导连接” 和 “常规操作”)
[1. 核心原则](#1. 核心原则)
[2. 详细解释](#2. 详细解释)
[3. 总结](#3. 总结)
[1. 核心区别对比](#1. 核心区别对比)
[2. 详细解释](#2. 详细解释)
[(1)Redis 哨兵模式:"问路式"](#(1)Redis 哨兵模式:"问路式")
[(2)MySQL Router 模式:"代理式"](#(2)MySQL Router 模式:"代理式")
[3. 总结与选择](#3. 总结与选择)
一、哨兵模式中客户端如何访问服务器
为了更直观地理解,我们首先通过一张时序图来俯瞰整个交互流程,然后再分步进行详细解释。

1. 详细步骤分解
整个过程可以分为三个阶段:故障发现与转移、客户端感知与切换、恢复正常访问。
(1)第一阶段:故障发现与自动转移(由哨兵集群自动完成)
这一步是哨兵模式的核心,在客户端无感知的情况下完成。
- 主观下线
每个哨兵节点会定期(每秒一次)向所有主节点、从节点以及其他哨兵节点发送 PING 命令。如果一个主节点在配置的 down-after-milliseconds 时间内未有效回复 PING(可能是返回错误如 LOADING,也可能是超时无响应),那么发现它的哨兵就会将其标记为 "主观下线"。这意味着单个哨兵认为这个主节点可能挂了。
- 客观下线
当一个哨兵将主节点标记为主观下线后,它会向其他哨兵节点发送 SENTINEL is-master-down-by-addr 命令,询问它们是否也认为该主节点下线了。如果超过法定数量(通常是哨兵总数的一半以上,比如 3 个哨兵需要 2 票)的哨兵都报告该主节点主观下线,那么发起询问的哨兵就会将这个主节点标记为 "客观下线"。这意味着从集群层面确认主节点确实故障了,可以开始执行故障转移。
- 选举领头哨兵
主节点被确认为客观下线后,所有哨兵会通过一种 Raft 共识算法来选举一个 "领头哨兵" 。选举规则包括互相投票,直到某个哨兵获得多数票。领头哨兵负责完成后续的故障转移操作,这避免了多个哨兵同时执行转移导致混乱。
- 故障转移
领头哨兵会从原主节点的从节点列表中,根据一定的规则(优先级 slave-priority、复制偏移量、Run ID 等)选出一个最优的从节点。领头哨兵向这个被选中的从节点发送 SLAVEOF NO ONE 命令,使其停止复制原主节点,并晋升为新的主节点。
领头哨兵然后向其他所有从节点发送 SLAVEOF 命令,让它们去复制这个新的主节点。
最后,领头哨兵会更新自己的配置,将旧主节点信息覆盖为新主节点信息,并通过发布/订阅功能通知整个哨兵集群。
(2)第二阶段:客户端感知与服务发现
当故障转移完成后,客户端需要知道新的主节点地址。
- 首次连接与订阅
在客户端启动时,它配置的不是 Redis 主节点的地址,而是多个哨兵节点的地址列表。客户端会随机选择一个哨兵节点进行连接,并执行 SENTINEL get-master-addr-by-name <master-name> 命令,来获取当前有效的主节点地址和端口。这里的 <master-name> 是在哨兵配置中定义的逻辑主节点名称。为了能持续感知主节点的变化,客户端还会向哨兵订阅 +switch-master 频道的事件。
- 感知变化与重连接
当发生故障转移后,领头哨兵会向 +switch-master 频道发布一条消息,内容包含了旧主节点和新主节点的地址信息。订阅了该频道的客户端会立即收到这条消息。客户端收到消息后,知道主节点已经变更,会主动关闭与旧主节点的连接,并使用新的主节点地址重新建立连接。
(3)第三阶段:恢复正常访问
- 命令重定向
在客户端成功连接到新的主节点之后,所有后续的读写命令(默认情况下,写命令和读命令都会发往主节点)都会发送到这个新的主节点上。如果客户端在故障转移的瞬间发送了命令,这些命令可能会失败(连接错误或超时)。一个健壮的客户端库应该具备重试机制,在捕获到异常后,会回到第二阶段,重新向哨兵查询主节点地址,然后重试命令。
2. 总结与关键点
- 解耦与高可用:哨兵模式将监控、通知、自动故障转移与数据存储分离开,实现了 Redis 的高可用。
- 客户端角色:客户端不再是简单的直连,而是作为一个主动的服务发现参与者。它需要与哨兵交互并监听事件。
- 不是强一致性:在故障转移的瞬间,可能会有少量数据丢失(原主节点宕机前未同步到新主的数据)。同时,客户端可能会遇到短暂的不可用或命令失败,需要重试。
- 健壮的客户端库:整个流程的顺畅度极大地依赖于客户端库的实现。一个好的 Redis 客户端库(如 Lettuce、Jedis 等)已经内置了对哨兵模式的支持,封装了上述的服务发现、订阅和重连逻辑,对应用开发者基本透明。
通过以上全过程,Redis 哨兵模式确保了在主节点发生故障时,集群能够自动恢复服务,从而保障了系统的高可用性。
二、"引导连接" 和 "常规操作"
为了进一步清晰说明客户端不是每次连接都通过哨兵找到主,我们区分一下"引导连接" 和 "常规操作"。
1. 核心原则
- 引导连接:当客户端第一次启动或完全丢失了主节点信息时,它必须通过查询哨兵来"引导"自己,找到当前的主节点地址。
- 常规操作:在成功引导之后,在没有发生故障转移的正常运行期间,客户端会直连已知的主节点,以获得最佳性能和最低延迟。
让我们通过一个修正后的流程图来展示这个更精确的过程,特别注意其中"直连主"与"问询哨兵"的两种连接路径:

2. 详细解释
(1)启动时:通过哨兵"问路"(必须通过哨兵)
当你的应用程序刚启动时,客户端的连接池里是空的,它不知道主节点是谁。这时它的行为是:
- 读取配置(通常是多个哨兵的地址列表)。
- 从中选一个可用的哨兵进行连接。
- 向该哨兵发送 SENTINEL get-master-addr-by-name <master-name> 命令。
- 哨兵返回当前的主节点 IP 和端口。
- 客户端拿到这个地址后,会缓存下来,并直接用这个地址与主节点建立连接。
所以,在第一次连接建立时,客户端是通过哨兵找到了主节点,然后直连主节点。
(2)运行时:直接访问(直连主)
在成功获取主节点地址并建立连接后,在没有发生故障切换的漫长稳定运行期内:
- 客户端的所有 GET、SET 等数据操作命令,都是直接发送给它缓存的那个主节点地址的。
- 它不会在每次执行命令前都去问一次哨兵。
- 这样做的好处是性能极高,没有中间层,延迟最低。如果每次命令都通过哨兵转发,哨兵会成为性能和单点故障的瓶颈,这就失去了 Redis 高性能的优势。
(3)故障时:再次"问路"(必须通过哨兵)
当主节点宕机,哨兵完成故障转移后:
- 哨兵会更新自己的主节点信息。
- 同时,它会向它负责的 +switch-master 频道发布一个消息。
- 客户端在启动时,除了查询主节点地址,还会订阅这个频道。
- 当客户端收到 +switch-master 事件后,它就知道了两件事:旧的主节点已经失效;新的主节点是谁(消息体中包含新地址)。
- 客户端会:关闭与旧主节点的连接池;用新的主节点地址重新建立直连;后续的所有命令又恢复为直连新的主节点。
如果客户端因为网络问题没有收到订阅消息,它会有降级策略:当发现与主节点的连接中断时,它会退回到初始的"引导连接"步骤,重新询问哨兵以获取新的主节点地址。
3. 总结
| 场景 | 访问模式 | 说明 |
|---|---|---|
| 应用启动/首次连接 | 通过哨兵查询,然后直连主 | 一次性的"服务发现"行为。 |
| 正常运行期间 | 直连主节点 | 常态,为了高性能。 |
| 故障转移之后 | 通过哨兵事件感知,然后直连新主 | 被动通知或主动重查,之后恢复直连。 |
在正常情况下,没有发生主从切换时,整个数据访问流程可以概括为:
- 初始化引导(一次性动作):客户端通过查询哨兵集群,获取当前主节点的真实地址。这个过程只在应用启动或重建连接池时发生。
- 长期稳定运行(常态):客户端将获取到的主节点地址缓存起来。此后,所有的 GET、SET、DEL 等数据操作命令,都直接发送给这个缓存的主节点地址。在此期间,哨兵集群完全处于后台静默状态,客户端与它们没有任何数据交互。数据通道是客户端与主节点之间的直连通道。
这样设计的核心目的就是为了极致的性能:
- 低延迟:消除了每次命令的额外网络跳转(如果经过哨兵,会多一跳)。
- 高吞吐:避免了哨兵成为数据平面的性能瓶颈。
- 架构清晰:将管理控制平面(哨兵,负责监控和切换)和数据平面(Redis 主从节点,负责数据处理)分离开。
三、服务发现与代理
Redis 哨兵模式和 MySQL Router 代表了服务发现与代理的两种核心思想。为了更直观地展示这一关键区别,我们可以用下图来对比:

1. 核心区别对比
| 特性 | Redis 哨兵模式 | MySQL Router |
|---|---|---|
| 架构模式 | 智能客户端 + 服务发现 | 代理层 + 透明转发 |
| 数据路径 | 客户端 -> 主节点 (直接) | 客户端 -> Router -> 主节点 (间接) |
| 客户端角色 | 主动,需要集成哨兵客户端逻辑 | 被动,只需将 Router 视为一个普通的 MySQL 服务器 |
| 性能 | 更低延迟,少一次网络跳转 | 略高延迟,多经过一次代理 |
| 复杂度 | 客户端逻辑复杂,需要处理连接切换 | 客户端逻辑简单,所有复杂性封装在 Router 中 |
| 耦合度 | 客户端与 Redis 服务发现机制耦合 | 客户端与 MySQL 集群架构解耦 |
2. 详细解释
(1)Redis 哨兵模式:"问路式"
比喻:就像您开车去一个目的地。您出发前先用导航(哨兵)查一下地址(主节点 IP),然后就直接开车到目的地了。除非道路施工或封路(主节点宕机),导航才会重新规划路线,您再开往新地址。
优点:
- 性能最佳:没有中间商赚差价,网络延迟最低。
- 架构简单:在稳定状态下,数据路径非常直接。
缺点:
- 客户端沉重:客户端需要实现服务发现、订阅事件、故障重试等逻辑,变得"智能"且复杂。
- 耦合性高:客户端必须知道哨兵的地址和协议,与 Redis 的基础设施紧密耦合。
(2)MySQL Router 模式:"代理式"
比喻:就像您叫了一辆专车。您每次都告诉司机(Router)您要去哪儿,由司机决定走哪条路、开哪辆车。您不需要关心后台是哪个 MySQL 实例在真正提供服务。
优点:
- 客户端简单:客户端像使用单点 MySQL 一样使用 Router,无需感知后端集群结构。数据库的扩缩容、主从切换对客户端透明。
- 集中控制:路由规则、负载均衡策略、故障转移都可以在 Router 层统一管理和配置。
- 解耦:客户端与数据库集群完全解耦。
缺点:
- 性能开销:所有流量都经过 Router,多了一次网络跳转,增加了微小的延迟,且 Router 本身可能成为性能瓶颈。
- 引入新组件:需要额外部署、监控和维护 Router 这个高可用组件。
Redis 哨兵模式 追求的是极致的性能和简单的数据路径,愿意将复杂性放在客户端。MySQL Router 模式 追求的是客户端的简单性和架构的透明性,愿意引入一个代理层来封装所有复杂性。
在现代微服务架构中,这两种模式也很常见:服务网格类似于 Router 模式,所有流量都经过 Sidecar 代理。很多服务发现框架则类似于哨兵模式,服务实例直接相互通信。
(3)风格比较
这两种设计风格没有绝对的优劣,只有是否适合特定的场景。它们是典型的 "性能与复杂度" 和 "简单性与可控性" 之间的权衡。
为了更直观地对比这两种风格的适用场景,我们可以参考以下决策矩阵:

- 核心权衡维度
- 性能 vs 延迟
-
直连模式胜出
-
理由:少一次网络跳转,数据路径最短。对于Redis这种对亚毫秒级延迟有极致追求的系统至关重要。
-
代理模式短板:所有流量都经过Router,必然引入额外的延迟和潜在的瓶颈。
-
- 客户端复杂度 vs 架构简单性
-
代理模式胜出
-
理由:客户端无需感知后端集群变化,行为像访问单点一样简单。所有复杂性(路由、负载均衡、故障转移)都封装在代理层。
-
直连模式短板:客户端必须集成"智能",实现服务发现、故障感知、重连逻辑,变得沉重且复杂。
-
- 可维护性与可观测性
-
代理模式胜出
-
理由:Router作为一个中心点,是绝佳的监控、流量控制、协议转换的切入点。可以集中实施安全策略、审计日志、限流等。
-
直连模式短板:客户端分散,难以实施统一的管控和观测。
-
- 技术栈灵活性与耦合度
-
代理模式胜出
-
理由:客户端与数据库技术栈解耦。更换数据库版本甚至类型,后端集群扩缩容,对客户端完全透明。
-
直连模式短板:客户端与特定的服务发现机制(如Redis哨兵协议)紧密耦合。
-
- 资源消耗与可扩展性
-
直连模式胜出
-
理由:连接是分散的,没有单点瓶颈。要服务更多客户端,只需要扩展后端资源。
-
代理模式短板:Router本身可能成为性能和连接数的瓶颈,需要自己做集群和高可用。
-
(4)现代架构的演进趋势
这两种模式也在不断演进和融合。
- Sidecar 模式(服务网格):这是两种模式的融合。每个客户端都部署一个轻量级代理(Sidecar)。对应用来说,它像是直连(localhost),非常简单。对架构来说,所有流量都经过代理,具备了集中管控的能力。实现了 "简单性" 和 "可控性" 的兼得,代价是更高的资源开销和架构复杂度。
- 智能负载均衡器:云服务商提供的数据库服务(如AWS RDS Proxy, Azure Database for MySQL)本质上就是一个全托管的、高度可用的"Router"。它将代理的复杂性从用户侧完全移除,提供了最佳的体验。
3. 总结与选择
选择直连模式(Redis 哨兵风格)当:
- 你对性能和数据延迟有极致要求。
- 你的客户端技术栈统一,并且有能力维护一个健壮的客户端库。
- 你的团队对底层基础设施有很强的掌控力。
- 典型场景:缓存、实时排行榜、消息队列、游戏会话存储。
选择代理模式(MySQL Router 风格)当:
- 你追求开发效率和应用侧的简单性。
- 你的客户端技术栈多样(如多种语言、多种框架),难以统一维护客户端逻辑。
- 你需要集中的可观测性、安全性和管控。
- 典型场景:核心业务数据库、ERP系统、多语言微服务后端。
总而言之,这是一个经典的工程设计权衡。Redis 生于一个对性能极度渴求的时代,其核心价值是"快",因此选择了直连。MySQL 作为一个成熟稳定的 OLTP 数据库,其核心价值是"可靠"和"通用",因此选择了通过代理来保证客户端的简单和架构的清晰。
在当今云原生时代,Sidecar 模式正在尝试"鱼与熊掌兼得",但它本身也带来了新的复杂性。没有完美的银弹,只有最适合你当前和可预见未来需求的选择。