这里结合我这些年实际运维和搭建 Redis 高可用架构的经验,把主从复制和哨兵模式这套东西从头到尾捋一遍。标题听起来是基础内容,但真要在生产环境里把它玩明白,里面还是有不少容易被忽视的细节。我尽量把原理、配置、实战和排障都讲透,适合正在搭建 Redis 高可用方案的开发者,也适合想彻底搞懂主从和哨兵底层逻辑的运维同学。
1. 架构方案与原理拆解
1.1 单机 Redis 到主从复制:究竟解决了什么
先想一个最简单的问题:一台 Redis 实例挂掉,缓存全丢,后端数据库瞬间被流量打穿,这在生产里是灾难性的。凡是核心链路用了 Redis,单机部署基本等于裸奔。主从复制最直观的价值就是冗余,主节点写,从节点同步数据,主节点挂了,从节点还能顶上,数据不丢或者少丢。
但这只是表象。主从复制更深层的价值,是把读压力从单点分散出去。我们很多实际业务是典型的读多写少,比如商品详情、用户会话、配置缓存,读 QPS 可能是写的几十倍。主节点只负责写,查询全部走从节点,整体吞吐一下就上去了。再往后做读写分离,从节点还能横向扩展。
还有一个容易被忽略的点:主从复制给“重启恢复”提供了安全垫。之前我在某项目里有台 Redis 需要升级版本,直接重启主节点肯定阻塞写入,但有了从节点,可以先让从节点顶上去,切换后老主重启、重新挂回主从,整个过程业务无感。
当然,主从复制不等于高可用。它解决的是“数据有备份”和“读能力扩展”,但故障发生时,谁来发现主节点挂了,谁来发起切换,业务怎么感知新主节点,这些主从本身回答不了。这就要交给哨兵。
1.2 主从复制底层逻辑:全量同步、增量同步、复制积压缓冲区
主从复制不是玄学,核心说白了就是“主节点把写操作同步给从节点”。但实现上有几个关键机制值得掰开讲。
全量同步发生在从节点第一次连接主节点,或者从节点断开太久导致复制偏移量已经不在主节点的积压缓冲区里。流程是:从节点发送psync命令,主节点收到后执行bgsave生成 RDB 快照,同时把快照生成期间的写操作缓存到复制积压缓冲区(repl backlog),然后主节点把 RDB 文件发给从节点,从节点清空旧数据并载入 RDB,载入完成后主节点再把积压缓冲区的增量命令发给从节点。整个链路完成后,主从进入增量同步阶段。
增量同步就是主节点把每一条写命令直接发给从节点,从节点顺序执行。这里有个关键概念:复制偏移量。主节点和从节点各自维护一个 offset,主节点每发送 N 字节数据,offset 加 N;从节点每接收并应用 N 字节,offset 也加 N。通过对比主从 offset 就能知道数据差了多少。我们排查主从延迟时,第一件事就是比对这两个值。
复制积压缓冲区是一个环形内存区域,默认大小 1MB。它的作用就是让从节点在短暂断线后能增量续传。但如果从节点断线时间太久,主节点的环形缓冲区被后续写操作覆盖,从节点 offset 对应的数据已经不存在了,那主节点只能做全量同步。这个缓冲区大小的设置直接决定了“允许从节点断线多久而不触发全量同步”。1MB 在生产环境基本不够用,我一般会把它调到 128MB 甚至更大,代价只是主节点多一点内存开销。
主从之间还会定期发送心跳检测网络状态,从节点默认每秒向主节点上报一次自身的复制偏移量,主节点通过INFO replication里lag字段能看出从节点多久没上报了。这个字段是判断主从链路健康度的重要指标。
1.3 为什么还要哨兵:高可用判定与自动切换
主从复制做好了,主节点照样可能因为机器宕机、进程崩溃、网络分区而不可用。这时候需要一个人工或者自动化的角色,去发现故障、协商新的主节点、通知客户端。哨兵本质上就是这个“监工”。
哨兵节点是独立部署的 Redis 进程,它做三件事:监控主从节点的存活状态、通知客户端主节点变化、在主节点故障时执行自动故障转移,选出一个从节点提升为新主,并让其他从节点重新指向新主。
这里有一个非常关键的设计思想:哨兵本身也要高可用,所以生产环境至少要部署 3 个哨兵节点,并且要部署在互相隔离的机器上。3 个哨兵节点通过投票机制判定主节点是否真的挂了,从而避免单个哨兵误判就触发切换。这个机制稍后会详细拆解。
对比主从复制,哨兵相当于给整个缓存层加上了“自愈能力”。业务从主节点挂了到新主节点选举完成,通常只需要十几秒。这个时间窗口内,如果业务做了多级缓存兜底或者数据库限流,用户体验基本无感。如果没有哨兵,主节点挂了就是等着人工上去操作,几分钟甚至更长时间的不可用,谁也扛不住。
2. 部署与配置实操:从零搭建主从复制
2.1 环境准备与基础配置
我这里以最常用的 Linux 服务器为例,假设你有两台机器,一台做主节点,一台做从节点。实际上 Redis 5.0 以上版本主从配置几乎没有差别,我目前主要用的是 6.x 和 7.x,配置项基本一致。
安装这部分不确定你用的是 apt 还是编译安装,所以只提醒三点:第一,系统内存要预留至少 30%,因为bgsave的 fork 会瞬时占用额外内存;第二,务必关闭透明大页(echo never > /sys/kernel/mm/transparent_hugepage/enabled),否则 RDB 持久化时可能出现严重的延迟和资源消耗;第三,修改内核参数vm.overcommit_memory = 1,避免 Redis 在持久化时因为内存申请被拒而失败。
主节点基础配置我一般会在redis.conf里先确认这几项:
bind:生产环境不要把 Redis 服务暴露到公网。如果你需要跨机房同步,就用内网 IP 或者走专线,不要直接用公网可访问的地址。protected-mode yes:开启保护模式。port 6379:默认即可,但最好保证主从端口一致,便于管理。requirepass:主从节点都要访问密码。如果不设置密码,在主从复制的场景里很容易被扫描攻击。appendonly yes:建议开启 AOF,主节点故障重启后能恢复更多数据。repl-backlog-size 128mb:调大复制积压缓冲区,避免短时间断连就触发全量同步。
从节点配置类似,但要多加一个replicaof参数:
replicaof 10.0.0.1 6379 masterauth YourStrongPasswordreplicaof后面跟主节点的 IP 和端口。masterauth是从节点连接主节点时使用的密码,这个必填,否则从节点会一直报MASTER <-> REPLICA sync started然后卡在NOAUTH。
2.2 三种常用的主从配置方式
方式一:修改配置文件
这种方式最稳定,适合长期部署。在从节点配置文件中加入上述的replicaof和masterauth,然后重启从节点服务即可。优点是一旦配置就不会丢失,缺点是修改配置要重启。
方式二:命令行动态设置
如果你不想重启 Redis,可以用命令直接在从节点上执行:
redis-cli -h 10.0.0.2 -p 6379 -a YourPassword replicaof 10.0.0.1 6379执行后立刻生效,用INFO replication就能看到主从状态。但这种方式在服务重启后会丢失,如果只是临时测试,用这个最快。
方式三:Docker 容器部署
现在很多团队用 Docker 部署 Redis,我见过很多人在这一步踩坑。最典型的错误是把主节点的 IP 写成了宿主机 IP,导致容器内不通。正确做法是使用 Docker 内部网络,或者用--network host模式简单粗暴。
使用主机网络模式:
# 主节点 docker run -d --name redis-master --network host \ -v /data/redis-master:/data \ -v /etc/redis/redis-master.conf:/etc/redis/redis.conf \ redis:7.0 redis-server /etc/redis/redis.conf # 从节点 docker run -d --name redis-replica --network host \ -v /data/redis-replica:/data \ -v /etc/redis/redis-replica.conf:/etc/redis/redis.conf \ redis:7.0 redis-server /etc/redis/redis.conf只要从节点配置文件里replicaof写的是主节点宿主机内网 IP,网络层面就会很干净。如果你用 bridge 网络,则要确保主节点的bind配置包含容器网段,否则从节点连不上。
2.3 验证主从同步与参数解读
配置完成后,在主节点和从节点分别执行:
redis-cli -a YourPassword INFO replication主节点输出大概是这样:
# Replication role:master connected_slaves:1 slave0:ip=10.0.0.2,port=6379,state=online,offset=123456,lag=0 master_replid:8f3c9a2b... master_repl_offset:123456 repl_backlog_active:1 repl_backlog_size:134217728 repl_backlog_first_byte_offset:1 repl_backlog_histlen:123456从节点输出:
# Replication role:slave master_host:10.0.0.1 master_port:6379 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_read_repl_offset:123456 slave_repl_offset:123456重点关注几个点:
role必须一个是master,一个是slave。master_link_status为up表示主从链路正常。offset主从一致,说明没有堆积。lag为 0 表示从节点刚刚上报过状态。
验证读写同步,简单粗暴的方法:主节点执行SET test:sync 1,从节点执行GET test:sync,能拿到值就说明通了。
还有一个经常被忽略的细节:从节点默认是只读的,你不能往从节点写数据,否则不仅写入失败,还会造成数据不一致。如果确实需要从节点在特殊场景下可写,可以通过replica-read-only no改,但正常人千万别这么干。
3. 哨兵模式搭建与核心参数调优
3.1 哨兵架构与基础配置
哨兵模式下,你的节点分为两类:Redis 数据节点(一主多从)和 Redis 哨兵节点(至少 3 个)。哨兵节点也是独立的 Redis 进程,但它不存储业务数据,只做监控和协调。
假定现在你有一主两从,架构是:
- Master:10.0.0.1:6379
- Replica1:10.0.0.2:6379
- Replica2:10.0.0.3:6379
- Sentinel1:10.0.0.11:26379
- Sentinel2:10.0.0.12:26379
- Sentinel3:10.0.0.13:26379
每个哨兵节点的配置sentinel.conf类似这样:
port 26379 sentinel monitor mymaster 10.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 15000 sentinel parallel-syncs mymaster 1 sentinel auth-pass mymaster YourStrongPassword关键项的作用:
sentinel monitor mymaster <ip> <port> <quorum>:mymaster是监控的主节点名称,可自定义,但要保持所有哨兵一致。最后一个2是 quorum,表示至少几个哨兵同意主节点主观下线,才会触发客观下线。sentinel down-after-milliseconds:哨兵判定主节点不可达的时间阈值,默认 30000 毫秒。生产建议 5000 到 10000 之间,设置太短容易误判,太长故障切换太慢。sentinel failover-timeout:故障转移超时时间,默认 180000。这个值要结合你的网络环境和切换复杂度调整,通常 15 秒左右够用。sentinel parallel-syncs:故障转移后,同时向新主节点发起同步的从节点数量。如果为 1,说明新的从节点挨个同步,避免新主节点瞬间负载过大。
所有哨兵节点配置一致,只是端口可以不同,生产建议端口统一用 26379。启动时使用:
redis-sentinel /etc/redis/sentinel.conf或者:
redis-server /etc/redis/sentinel.conf --sentinel启动后,哨兵会自动发现彼此,并监控主从状态。你可以在任意哨兵节点执行:
redis-cli -p 26379 sentinel masters redis-cli -p 26379 sentinel replicas mymaster查看当前的 Master 是谁,以及有哪些从节点。
3.2 主观下线、客观下线、选举与故障转移流程
要理解哨兵工作流程,必须分清两个概念:主观下线(Subjectively Down,SDOWN)和客观下线(Objectively Down,ODOWN)。
主观下线:单个哨兵发现主节点在down-after-milliseconds时间内没有响应 PING,就把主节点标记为主观下线。这只是这个哨兵自己的看法,不一定代表主节点真挂了。
客观下线:当标记 SDOWN 的哨兵向其他哨兵询问主节点状态,如果收到足够数量(quorum)的哨兵确认主节点不可达,主节点就被标记为客观下线。也就是说,至少 quorum 个哨兵都认为主节点有问题,才准备故障转移。
举个例子,quorum 配置为 2,3 个哨兵中只有 1 个哨兵认为主节点挂了,那么主节点不会进入客观下线状态。只有 2 个及以上哨兵同时认为主节点挂了,才会触发后续流程。这个机制的最大价值是防止网络抖动导致单个哨兵误判。
客观下线之后,哨兵集群要做的就是领导者选举。哨兵集群会选举出一个 leader 哨兵来主导故障转移流程。这个选举是基于 Raft 算法的变体,简单理解就是在所有哨兵节点中通过投票选出某一个节点作为执行者。每个哨兵节点在发现客观下线后,会向其他哨兵发送命令请求对方投票,获得超过半数投票的哨兵成为 leader。这个设计保证了故障转移只有一个执行者,不会出现多个哨兵同时操作导致的数据混乱。
Leader 哨兵接下来执行故障转移,步骤是:
从当前从节点列表中选出一个作为新主节点。选举的优先级先看
slave-priority配置,数字越小优先级越高,默认 100。如果优先级相同,再比较复制偏移量,偏移量越大的说明数据越新。如果偏移量也相同,再比较 run_id,字典序更小的胜出。我们生产环境有一条铁律:不要把某些机器上的从节点 priority 设成 0,因为 0 永远不参与选举。向选中的从节点发送
slaveof no one,让这个从节点成为独立主节点。把其他从节点重新指向新主节点,通过
slaveof <new-master-ip> <port>。监视原来的旧主节点。如果旧主节点恢复并重新加入,哨兵会把它降级为从节点,并让它复制新主节点的数据。
整个过程业务无感知是不可能的,但从写请求失败到新主节点对外提供写服务,通常能在 10 到 20 秒内完成,前提是你的参数调得合理。
3.3 核心参数调优建议
我实测过很多组参数,下面这些调优建议是基于线上大规模业务验证过的。
quorum 的设置:虽然官方推荐 quorum = 哨兵数量/2 + 1,但实际场景里,你要权衡误判和切换速度。如果哨兵有 5 个,quorum 设 2 就够了,事实上 quorum 只需要大于等于 1 并且小于哨兵多数即可。推荐生产环境 3 个哨兵、quorum 2,这样既能容忍一个哨兵挂掉,又不至于因为需要太多结点达成一致而拖慢切换。
down-after-milliseconds 不建议太长:很多人为了降低误判,把这个值设置成 60000 或者 120000。但你的主节点真挂了,整个缓存写入要等 1 到 2 分钟才能切换,业务早就超时了。我一般设置 5000 到 10000。如果你使用的是云服务器,网络抖动相对少,5000 足够。
parallel-syncs 的设置:如果从节点数量很多,一次性全部同步新主节点,可能在同步高峰期给新主节点带来巨大压力。所以建议并行同步数从 1 开始,观察新主节点负载。如果新主节点负载很低,再逐步调大。我一般设置为 2。
failover-timeout 的设置:这个参数影响故障转移的最终超时时间,包括选举 leader 的时间、从节点切换时间、旧主节点恢复后的重新配置时间。默认 180 秒偏大,我习惯设在 15 到 20 秒。如果切换期间出现异常,哨兵会在超时后重新尝试整个流程。
sentinel resolve-hostnames与 Docker 场景:如果你在 Docker 里跑哨兵,容器间用服务名通信,需要开启sentinel resolve-hostnames yes,否则哨兵看到的是容器的随机 ID,会导致监控失败。
4. 常见问题、坑点与实战经验
4.1 主从数据分叉、延迟与堆积
主从延迟是最常见的问题。表现为从节点master_repl_offset和主节点master_repl_offset相差越来越大。造成延迟的主要原因有三个:
第一,主节点写入量巨大,从节点应用命令的速度跟不上。这种情况最直接的表现是 CPU 在从节点上跑满,因为从节点要逐个执行写命令。解决方案是拆分主节点上的写入压力,或者减少从节点数量,避免一个主节点带太多从节点。
第二,大 Key 操作导致复制阻塞。比如在主节点执行一个KEYS *之类的 O(N) 操作,Redis 是单线程模型,这个操作会阻塞整个主线程,复制命令也会被阻塞。还有对大 Key 执行SETBIT、SORT这种耗时的操作,会让从节点也经历同样的耗时,进而造成延迟。经验是尽量拆分大 Key,比如把大的 Hash 拆成多个小 Hash。
第三,网络带宽瓶颈。主从复制是全量命令流式传输,如果主节点带宽不足,同步就会变慢。尤其在做全量同步时,RDB 文件可能几个 GB,非常考验网络。
我排查延迟时有一个固定的套路:先从INFO replication里看lag,如果lag持续增长,说明从节点没有及时上报。然后在从节点看redis-cli --latency或者直接看INFO cpu,再用SLOWLOG GET看看有没有慢命令。如果慢命令发生在主节点,那从节点一定会被波及。
4.2 哨兵误判和脑裂问题
哨兵误判很好理解:主节点没有挂,只是因为瞬时负载过高或者网络抖动,导致哨兵收不到 PONG,就触发了一次不必要的切换。切换本身有成本,新主节点数据可能不是最新的,业务侧连接也需要重连。防止误判除了调大down-after-milliseconds,还可以在哨兵配置里加上sentinel config-epoch相关的限制,但根本手段是确保哨兵节点和主节点在同一个可靠的内网环境里。
脑裂问题更隐蔽。打个比方,如果主节点所在机器网络出问题,主节点自己并不知道自己被分区隔离了,客户端还会继续往这个主节点写数据;而哨兵因为联系不上主节点,已经选出了新主节点,并且让其他从节点切换到了新主。此时,旧主节点还在接收写入,但这些写入无法同步给新主,等旧主恢复网络后,它会认为自己是主节点,于是出现了两个主节点的局面,数据面临分叉风险。
Redis 官方解决脑裂的方案是min-replicas-to-write和min-replicas-max-lag这两个配置。在主节点上设置最少需要几个从节点在线、且从节点最大延迟不超过多少秒,才允许写入。例如:
min-replicas-to-write 1 min-replicas-max-lag 10意思是如果主节点连不到任何一个从节点,或者所有从节点都延迟超过 10 秒,主节点就拒绝写入,以此避免脑裂期间的无谓写入。当然这个配置会降低可用性,你要根据业务权衡。我通常在核心缓存场景开启这两个参数,宁可损失一部分写可用性,也不要出现数据分裂。
4.3 客户端如何感知主从切换
主从复制和哨兵完全部署好之后,业务代码如果还在直连一个固定 IP 的 Redis 地址,那一切高可用就白做了。客户端必须通过哨兵感知当前主节点。
以 Java 生态最常用的 Jedis 为例,你需要使用JedisSentinelPool:
Set<String> sentinels = new HashSet<>(); sentinels.add("10.0.0.11:26379"); sentinels.add("10.0.0.12:26379"); sentinels.add("10.0.0.13:26379"); JedisPoolConfig poolConfig = new JedisPoolConfig(); try (JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels, poolConfig)) { try (Jedis jedis = pool.getResource()) { jedis.set("foo", "bar"); String value = jedis.get("foo"); } }客户端在做连接初始化时,会向哨兵发送sentinel get-master-addr-by-name mymaster获取当前主节点地址。当主节点发生切换,客户端旧连接会报错,但JedisSentinelPool内部的监听器会通过哨兵重新获取新主节点地址,自动建立新连接。
如果是 Spring Data Redis,则需要配置spring.redis.sentinel.master=mymaster和spring.redis.sentinel.nodes=10.0.0.11:26379,10.0.0.12:26379,10.0.0.13:26379。
这里必须提醒一个常见坑:客户端连接哨兵只能用哨兵地址,不能用 Redis 数据节点地址。如果你把数据节点地址传给客户端,主从切换之后,客户端就会一直连旧节点,轻则读写失败,重则产生脏数据。另外,哨兵可以不配置在业务机器上,但必须和业务网络互通。
4.4 密码认证、复制超时与 Docker 场景的实战坑
先说说密码认证这个最容易踩坑的地方。主节点配置了requirepass,但是创建主从连接时,从节点需要携带masterauth密码。如果没有设置或者密码错误,你会看到从节点日志里反复出现:
MASTER <-> REPLICA sync started Error reply to PSYNC from master: -NOAUTH Authentication required.这种错误极常见,我遇到过不下三次。排查思路很简单:从节点能连上主节点吗?再用AUTH命令验证密码是否正确。另外,哨兵访问主从节点也需要认证,除了sentinel monitor里填的密码,还要给哨兵配置sentinel auth-pass,否则哨兵会连接不上数据节点,导致一直处于NOINFO状态。
复制超时也是一个高频问题。Redis 有repl-timeout参数,默认是 60 秒。它决定了从节点多久没有收到主节点的有效数据就算超时。如果你网络环境不太好,或者全量同步过程中数据量大,容易在复制时触发超时。我一般会把repl-timeout调大到 120 秒,避免因为临时网络波动导致全量同步失败。
Docker 场景下还有一个隐藏比较深的点:如果你用默认的 bridge 网络启动 Redis 主从,从节点复制到一半时崩溃了,重新创建容器,可能因为动态端口映射导致主从无法互相访问。所以我强烈建议 Redis 主从和哨兵都不要做端口映射,直接用--network host。除非你非常了解 Docker 网络的端口暴露机制,否则别在这上面浪费时间。
这里再补充一个节点部署规划上的经验:哨兵节点应该部署在至少 3 台不同的物理机或者云主机的不同机架上,而不是同一台机器上跑 3 个哨兵进程。如果都在一台机器上,这台机器整体宕机,哨兵集群瞬间失去多数派,故障转移就没法进行。很多人都知道 3 哨兵,但往往部署在同一台服务器上,这属于典型的自欺欺人式高可用。
4.5 从节点晋升新主之前,先看数据完整性
故障切换发生的时候,哨兵会选一个从节点提升为主节点。但如果这个从节点之前就已经断开很久,数据落后主节点很多,那么把它提升为主节点后,数据丢失问题会被放大。
我排障时见过一个实际案例:有一组一主两从,其中一个从节点因为网络问题断连了几小时,另外两个正常。后来主节点挂了,哨兵居然把那个断连很久的从节点选成了新主,大量未同步的数据全部丢了。原因就是我没有调slave-priority,岁月静好时大家都一样,故障时就原形毕露。所以在生产环境,一定要给数据最全的从节点设置较低的优先级,比如:
- 数据最全的从节点:
slave-priority 10 - 备份用途的从节点:
slave-priority 100 - 建议不要出现
slave-priority 0,除非你明确知道这个节点永远不会参与选举。
但要注意,优先级只是偏好,最终选择还会结合 offset 等综合因素。你也应该在监控层面持续观测从节点的复制延迟,一旦发现某个从节点滞后严重,就不能让它参与故障转移选主。
4.6 实际操作中的排障速查表
把上面这些常见问题整理成速查表,方便你出问题时对照排查:
| 现象 | 可能原因 | 排查命令/处理方式 |
|---|---|---|
| 从节点连接不上主节点 | 网络不通、密码错误 | 在从节点执行redis-cli -h <主IP> -p 6379 ping验证网络;检查masterauth |
从节点一直处于down | master_link_status:down | 查看从节点日志,确认主节点是否设置了requirepass且masterauth正确 |
| 主从 offset 相差较大 | 大 Key、网络问题、主节点写压力大 | INFO replication看 offset,SLOWLOG GET看主节点慢命令,用redis-cli --bigkeys检查大 Key |
| 哨兵无法感知主节点状态 | 哨兵没有配置auth-pass、主节点启用了保护模式 | 在哨兵执行INFO sentinel,确认是否能嗅探到主从信息 |
| 哨兵频繁触发故障切换 | down-after-milliseconds太小、网络抖动 | 查看sentinel.log,根据抖动频率调大down-after-milliseconds |
| 主从切换后客户端连不上新主 | 客户端仍连接旧数据节点地址 | 改用哨兵模式客户端,如JedisSentinelPool |
| 旧主节点恢复后数据不一样 | 脑裂,写入没有被同步 | 配置min-replicas-to-write和min-replicas-max-lag,避免孤主写入 |
| 哨兵日志显示无法配对 | Docker 网络端口映射导致 | 直接用 host 网络,或正确设置容器间可达 IP |
这套速查表是我日常运维里反复用到的。遇到问题不要一上来就怀疑 Redis 本身有问题,先从网络、密码、配置、客户端这四个层面排查,90% 的坑都能在这里面找到。
5. 动手验证一下:手动模拟故障切换流程
理论讲了一堆,不动手测一测等于没学。这里给你一个可以跟着操作的验证实验,我在测试环境已经跑过很多遍。
5.1 搭建最小高可用环境(一主两从三哨兵)
先准备好 6 个节点,可以是 6 个 Docker 容器,也可以是 6 个虚拟机,规模小点无所谓。假设都跑在同一台 Linux 上,直接使用 host 网络模式。
主节点配置redis-master.conf:
port 6379 daemonize yes requirepass redispass masterauth redispass appendonly yes repl-backlog-size 128mb两个从节点配置redis-replica-1.conf和redis-replica-2.conf:
port 6380 daemonize yes replicaof 127.0.0.1 6379 masterauth redispass requirepass redispassport 6381 daemonize yes replicaof 127.0.0.1 6379 masterauth redispass requirepass redispass三个哨兵配置sentinel-1.conf:
port 26379 daemonize yes sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 15000 sentinel parallel-syncs mymaster 1 sentinel auth-pass mymaster redispass另外两个哨兵端口改为 26380 和 26381,其余一致。
然后依次启动:
redis-server redis-master.conf redis-server redis-replica-1.conf redis-server redis-replica-2.conf redis-sentinel sentinel-1.conf redis-sentinel sentinel-2.conf redis-sentinel sentinel-3.conf用redis-cli -p 26379 sentinel master mymaster查看当前主节点,应该能看到10.0.0.1或者127.0.0.1:6379为主。
5.2 杀掉主节点,观察自动切换
执行:
redis-cli -p 6379 shutdown nosave模拟主节点宕机。然后等大概 10 到 15 秒,再查询哨兵:
redis-cli -p 26379 sentinel master mymaster你会发现主节点已经变成了某个从节点的地址。再查该从节点:
redis-cli -p 6380 INFO replication可以看到role:master。而原来那个旧主节点如果重新启动,会自动变为新主节点的从节点。
这个过程建议多跑几次,观察整个切换时长和日志输出。你会注意到,如果down-after-milliseconds设得太小,比如 1000,在虚拟机环境里很容易因为瞬时 CPU 占用触发误切换。这也是为什么调这个参数要在稳定环境下反复测试。
5.3 验证客户端自动感知
可以用一个小脚本验证客户端是否自动感知主从切换。比如用 Python 的redis-py连接哨兵:
from redis.sentinel import Sentinel sentinel = Sentinel([('127.0.0.1', 26379)], password='redispass', socket_timeout=0.5) master = sentinel.master_for('mymaster', password='redispass') slave = sentinel.slave_for('mymaster', password='redispass') print(master.set('test', 'value')) print(slave.get('test'))先正常执行,然后杀掉主节点,等切换完成后再执行写入和读取,你会发现命令照样能成功,连接自动转移到了新的主节点。如果你的客户端没有配置哨兵模式,这一步骤就会报连接失败,这能很直观地说明客户端接入哨兵的必要性。
6. 最后一个实战层面的建议
写到这里,整个主从复制和哨兵模式的逻辑基本就完整了。这套方案并不复杂,但仍然有人在生产环境用了很久才发现从节点一直同步失败,或者故障切换后业务直接超时。我个人觉得,真正把这里面的原理吃透,比机械敲配置更重要。你知道全量同步和增量同步的区别,才知道为什么 backlog 要调大;你理解主观下线和客观下线,才知道为什么哨兵数量要是奇数,并且 quorum 不能乱填。
最后再分享一个小技巧:把哨兵也纳入监控报警范围,不能只关注 Redis 数据节点的可用性。我们用运维监控系统每 10 秒检查一次哨兵进程状态,并定期执行sentinel master mymaster获取当前主节点和从节点状态。如果发现主节点被切换了,会立刻通知相关人员排查切换原因,是优化变更还是真实故障。很多团队故障转移跑通了,但从不记录切换事件,等到月底一查才发现上周悄悄切了三次主,数据都不知道丢了多少。监控做到位,这套架构才算真正的落地。
如果后续还需要扩展,可以在这个基础之上做多机房部署,或者用 Redis Cluster 解决分片和高可用的问题。但一定要记住,Cluster 也不是万能的,数据分片后的跨 slot 操作有诸多限制。先把主从加哨兵玩熟,再往上走会顺畅很多。