☰
Redis哨兵高可用架构实战:Docker搭建一主两从三哨兵及故障转移演练
2026/10/21 15:26:00 网站建设 项目流程

不少人搭过Redis主从复制,觉得“数据有备份了”就算高可用了。直到某天凌晨主节点突然宕机,自己睡眼惺忪地登录服务器,手动执行slaveof no one、再让其他从节点重新指向新主,折腾完天都快亮了,才意识到这离“高可用”还差着十万八千里。Redis哨兵(Sentinel)就是来解决这个问题的:它帮你盯着主从拓扑,主节点一旦掉线,自动完成新主选举和从节点重指向,整个过程不用人碰。本文我用Docker把一套“一主两从三哨兵”的最小高可用架构完整跑了一遍,从网络规划、配置编写到故障切换演练,全部记录在案,连踩过的坑一起打包分享,适合刚接触Redis高可用、想快速验证Sentinel机制的读者。

这套环境我在Docker里反复搭过、拆过、破坏过好几次,整体跑通后我对“哨兵到底在干什么”的理解才算真正落地。下面的内容纯按我的实际操作顺序来写,你可以直接照抄,也可以根据自己环境调整。

1. 为什么需要哨兵:主从复制时代的高可用困境

1.1 主从复制解决了数据冗余,但没解决“谁来切换”

主从复制是Redis高可用的第一步。主库负责写,从库通过replicaof/slaveof命令异步同步数据,读流量可以分摊到从库,主库出问题时数据还有副本兜底。听起来不错,但有个很尴尬的问题:如果主库进程崩溃、机器断电,或者网络分区把主库隔开了,从库只会傻傻地等,不会自己转正,也不会自动把新主信息告诉客户端。业务还在往旧主地址上写数据,写入要么失败,要么超时,等到运维人工介入,服务已经中断了十几分钟甚至更久。

这种“被动等待人工”的状态在生产环境里是致命的。我见过不止一次,因为切换不及时、切换时手抖选错从库,导致数据不一致甚至丢数据的例子。主从复制像是给系统上了保险,但理赔的时候还需要人工跑一趟流程,保险的价值就打了折扣。

1.2 哨兵到底在做什么:监控、通知、自动故障转移

哨兵本质上也是一个Redis服务,只不过跑在特殊模式下(redis-server --sentinel)。它做四件事:

  • 监控:持续检查主库和从库是否在线,通过PING和INFO命令维持心跳。
  • 通知:当被监控的Redis实例状态变化时,哨兵之间通过内部频道互相通报。
  • 自动故障转移:主库被判定为客观下线后,哨兵会在存活从库中选出一个提升为新主,并让其他从库改为同步新主。
  • 配置提供:客户端可以连上哨兵,询问当前主库的地址是谁,而不是把主库地址硬编码在客户端配置里。

这里最核心的价值是“自动转移”加“客户端发现”。业务不用写死主库IP,而是启动时先问哨兵“现在谁是老大”,拿到地址后再连接。主库切换了,客户端重启后拿到的就是新主地址。

1.3 为什么用Docker来搭这套环境

我在本机用原生方式装过Redis,也在生产环境手工部署过哨兵,但Docker的体验还是最顺手的。原因有三点:

  • 环境一致:同事之间共享一个compose文件,跑出来的拓扑一模一样,不会出现“我本机可以你不行”的尴尬。
  • 快速重建:配置改坏了、数据写乱了,删掉容器重来,一分钟恢复初始状态,特别适合反复演练故障场景。
  • 网络可控:通过自定义bridge网络,给每个容器分配固定IP,模拟多机部署的效果非常方便。

当然Docker也带来了新的麻烦,比如网络模式的选择、容器重启后的状态保持、端口映射与哨兵通告地址的冲突,这些我后面会专门讲一遍。

2. 哨兵架构拆解:一主两从三哨兵的设计逻辑

2.1 为什么最少要三个哨兵节点

很多人看过文档都说“哨兵要奇数个”,但未必清楚背后的投票机制。哨兵判定主库故障分成两步:单个哨兵发现联系不上主库,标记为“主观下线”(sdown);随后哨兵之间互相确认,当同意下线的哨兵数量达到配置里的quorum值,才升级为“客观下线”(odown)。

客观下线只是“可以开始故障转移”的条件之一。真正要执行故障转移时,哨兵之间还要进行一次Leader选举,相当于选出一个“总指挥”来负责切换。这个选举要走多数派投票,也就是说候选哨兵必须拿到超过半数的选票才能当选。如果整个集群只有2个哨兵,投票结果天然容易僵持;而3个哨兵时,最多允许1个哨兵掉线,剩下2个依然能达成多数派,决策流程才能继续走下去。

所以“3个哨兵”是在保证容错能力和资源成本之间的平衡点。4个也能跑,但配置复杂度和节点成本上升,容错能力却没有本质提升,生产环境大多数场景下“一主两从三哨兵”就是性价比较高的经典组合。

2.2 数据流与角色分工:谁在跟谁说话

这套拓扑里的角色和通信关系理顺了,后面配置才不会乱。主库(master)负责写,从库(replica)异步同步主库数据并分担读流量。三个哨兵(sentinel)之间通过Redis的发布订阅频道(__sentinel__:hello)互相发现,定时交换彼此的信息;同时每个哨兵都保持着到主库和所有从库的TCP连接,用于发送PING、拉取INFO。

当主库心跳超时,哨兵发起故障转移后,它会向从库发送replicaof new_master命令,把从库重新指向新主,旧主恢复后也会被哨兵用同样的方式归队。整个切换过程中,客户端如果配置了哨兵发现机制,在旧主不可达时会重新向哨兵查询新主地址,无需手动干预。

2.3 容器编排拓扑:一张表看清角色与IP分配

我建议的容器拓扑如下表,所有容器都放在同一个自定义bridge网络里,并使用固定IP,避免重启后IP漂移。

容器名角色容器内端口固定IP数据目录
redis-master主库6379172.20.0.10./master/data
redis-slave-1从库16379172.20.0.11./slave1/data
redis-slave-2从库26379172.20.0.12./slave2/data
sentinel-1哨兵126379172.20.0.20./sentinel1/data
sentinel-2哨兵226379172.20.0.21./sentinel2/data
sentinel-3哨兵326379172.20.0.22./sentinel3/data

所有容器都不映射端口到宿主机,只在Docker内部网络互通。这样做的好处是彻底绕开端口映射带来的“地址通告错乱”问题,业务容器只要加入同一个网络,就能直接用redis-master:6379这种域名访问集群。如果你需要从宿主机外部调试,后面我会单独说怎么处理。

3. Docker网络规划:自定义Bridge、固定IP与端口映射的取舍

3.1 为什么不用host网络

Docker提供了多种网络模式,很多人图省事直接network_mode: host,让容器和宿主机共享网络栈。在Linux服务器上这确实省心,哨兵、Redis都直接用宿主机IP通信,没有地址转换,也不存在NAT问题。但有两个隐患:

  • Docker Desktop(macOS和Windows)不支持host网络,你在本机跑会直接报错。
  • host模式下容器端口和宿主机端口完全混在一起,如果宿主机上还跑着其他Redis实例,端口冲突会让你焦头烂额。

所以我更推荐自定义bridge网络。它相当于给容器搭了一个局域网,容器之间可以用IP或容器名直接通信,和外部隔离得比较干净,也可以随时拆掉重建,适合本地演练和CI环境。

3.2 固定IP vs 容器名解析,我为什么选固定IP

在同一个Docker compose网络里,容器名本身就是可解析的域名,比如在redis-slave-1容器里可以直接ping redis-master。乍一看,哨兵配置里的sentinel monitor mymaster redis-master 6379 2也能工作,因为哨兵容器能解析容器名。

但在实际操作中,我发现固定IP更让人安心。原因在于哨兵会把发现的从库、其他哨兵、以及当前选举世代(epoch)等信息写进自己的配置文件。如果master容器被删除重建,它的IP可能变了,而哨兵配置里保存的known-replica等信息可能还指向旧IP,导致拓扑错乱。固定IP让这些动态记录始终指向同一批地址,排查问题时少一个变量。

3.3 自定义网络的子网选择与避坑

创建一个自定义网络时需要手动指定子网,比如我用的是172.20.0.0/24。这里有个前提:宿主机现有网络不能和这个网段冲突,否则容器访问局域网资源时会出现路由混乱。我自己就遇到过宿主机局域网正好在172.20.x.x段的情况,容器内连公司内网服务全部失败,排查了很久才发现是网段撞车。建议先用ip addr或ifconfig看一眼宿主机网段,再挑一个不冲突的私网段。

配置compose时,网络定义要写成这样:

networks: redis-sentinel-net: driver: bridge ipam: config: - subnet: 172.20.0.0/24

然后每个服务里通过ipv4_address指定固定IP。这样整个网络拓扑一目了然,也方便后续在哨兵配置或客户端配置里直观引用。

4. 三个配置文件的完整编写:从主从到哨兵的参数拆解

4.1 主从Redis配置:容器里必须改的几个点

先看主库的master/redis.conf:

bind 0.0.0.0 port 6379 protected-mode no appendonly yes appendfsync everysec dir /data

和宿主机部署相比,容器里有两个点必须注意:

  • bind 0.0.0.0是必须的。如果按很多教程默认只绑定127.0.0.1,容器外部(包括其他容器)根本无法连接这个Redis。
  • protected-mode在非回环连接时会拦截请求,必须显式设为no。其实如果配置了密码,也可以保持默认并加上requirepass,但初学阶段建议先关掉,跑通后再加密码。

从库配置文件类似,只是多了一行replicaof指令。以slave1/redis.conf为例:

bind 0.0.0.0 port 6379 protected-mode no replicaof 172.20.0.10 6379 appendonly yes appendfsync everysec dir /data

这里我把replicaof写死成了master的固定IP172.20.0.10。前面强调固定IP的价值就在这里:即使容器重建、从库重启,这个地址仍然有效。dir /data配合Volume挂载,让RDB和AOF文件持久化到宿主机,避免容器销毁后数据全部蒸发。

4.2 哨兵配置:核心参数逐个拆开讲

哨兵配置文件比Redis主从配置稍复杂一点,但核心参数就那么几个。下面是sentinel1/sentinel.conf,另外两个哨兵除了端口一样,内容和这个基本一致(我稍后会说明差异):

bind 0.0.0.0 port 26379 protected-mode no dir /data sentinel monitor mymaster 172.20.0.10 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 30000 sentinel parallel-syncs mymaster 1

逐行解释一下:

  • sentinel monitor:监控对象名叫mymaster,目标主库地址是172.20.0.10:6379,最后的2是quorum。quorum=2表示至少两个哨兵都认为主库主观下线,才确定它是客观下线。
  • down-after-milliseconds:哨兵连续多少毫秒联系不上目标就认为它主观下线。我为了演练方便设成了5000毫秒,生产环境建议设大一点,比如30000毫秒,避免网络抖动就触发切换。
  • failover-timeout:故障转移的总超时时间。超时后如果还没完成,哨兵会重新发起一轮选举。
  • parallel-syncs:新主上位后,同时允许多少个从库与新主做全量同步。设成1的好处是逐个同步,避免新主瞬间被多个从库的全量同步请求压垮;如果从库不多,也可以设成2或3。

另外两个哨兵的配置几乎一样,唯一要注意的是如果哨兵文件里有sentinel known-replica这类动态信息,它们会各自记录自己的观察视角,无需手动干预。

4.3 Docker Compose编排:一份可以直接抄的模板

目录结构我建议这样组织:

redis-sentinel-demo/ ├── master/ │ ├── redis.conf │ └── data/ ├── slave1/ │ ├── redis.conf │ └── data/ ├── slave2/ │ ├── redis.conf │ └── data/ ├── sentinel1/ │ ├── sentinel.conf │ └── data/ ├── sentinel2/ │ ├── sentinel.conf │ └── data/ ├── sentinel3/ │ ├── sentinel.conf │ └── data/ └── docker-compose.yml

对应的docker-compose.yml如下:

version: "3.8" services: redis-master: image: redis:7.0 container_name: redis-master command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./master/redis.conf:/usr/local/etc/redis/redis.conf - ./master/data:/data networks: redis-sentinel-net: ipv4_address: 172.20.0.10 redis-slave-1: image: redis:7.0 container_name: redis-slave-1 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./slave1/redis.conf:/usr/local/etc/redis/redis.conf - ./slave1/data:/data depends_on: - redis-master networks: redis-sentinel-net: ipv4_address: 172.20.0.11 redis-slave-2: image: redis:7.0 container_name: redis-slave-2 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./slave2/redis.conf:/usr/local/etc/redis/redis.conf - ./slave2/data:/data depends_on: - redis-master networks: redis-sentinel-net: ipv4_address: 172.20.0.12 sentinel-1: image: redis:7.0 container_name: sentinel-1 command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"] volumes: - ./sentinel1/sentinel.conf:/usr/local/etc/redis/sentinel.conf - ./sentinel1/data:/data depends_on: - redis-master - redis-slave-1 - redis-slave-2 networks: redis-sentinel-net: ipv4_address: 172.20.0.20 sentinel-2: image: redis:7.0 container_name: sentinel-2 command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"] volumes: - ./sentinel2/sentinel.conf:/usr/local/etc/redis/sentinel.conf - ./sentinel2/data:/data depends_on: - redis-master - redis-slave-1 - redis-slave-2 networks: redis-sentinel-net: ipv4_address: 172.20.0.21 sentinel-3: image: redis:7.0 container_name: sentinel-3 command: ["redis-sentinel", "/usr/local/etc/redis/sentinel.conf"] volumes: - ./sentinel3/sentinel.conf:/usr/local/etc/redis/sentinel.conf - ./sentinel3/data:/data depends_on: - redis-master - redis-slave-1 - redis-slave-2 networks: redis-sentinel-net: ipv4_address: 172.20.0.22 networks: redis-sentinel-net: driver: bridge ipam: config: - subnet: 172.20.0.0/24

有两个细节值得说明。第一,哨兵镜像也可以直接用redis:7.0,只是启动命令换成了redis-sentinel。第二,depends_on只能保证容器启动顺序,不能保证Redis主库真正就绪,但从库的replicaof会自动重连,所以就算顺序错了也能自愈。如果你追求更严谨,可以在compose里加healthcheck,这里为了保持配置简洁就不展开了。

5. 启动编排与拓扑验证:让三组容器各就各位

5.1 启动前的权限检查

一个很容易翻车的点是文件权限。哨兵进程在运行时会把选举纪元(epoch)、已发现的从库和哨兵列表写回sentinel.conf,所以挂载进容器的配置文件必须可写。如果宿主机上的sentinel.conf权限是只读,启动后日志里会报Can't rewrite config file之类的错误,而且你会发现哨兵之间始终无法正确发现彼此。

建议启动前先执行一次:

chmod -R 777 ./sentinel1 ./sentinel2 ./sentinel3 ./master ./slave1 ./slave2

虽然777不够严谨,但本地演练完全够用,重点是先把环境跑起来。

5.2 启动命令与日志检查

一切就绪后,在redis-sentinel-demo目录下执行:

docker compose up -d

容器全部启动后,先看主从是否正常工作:

docker exec redis-master redis-cli info replication

正常输出里会显示role:master,下面列出两个从节点的IP和端口:

# Replication role:master connected_slaves:2 slave0:ip=172.20.0.11,port=6379,state=online,offset=... slave1:ip=172.20.0.12,port=6379,state=online,offset=...

如果connected_slaves显示为0,多半是从库的replicaof配置写错了,或者主库bind 0.0.0.0没配,从库根本连不上主库。

5.3 数据同步验证:写主库、读从库

再做一次最简单的数据验证。在主库写入一个key:

docker exec redis-master redis-cli set hello world

然后在两个从库上读:

docker exec redis-slave-1 redis-cli get hello docker exec redis-slave-2 redis-cli get hello

如果都能返回world,说明主从复制链路正常。这一步看似简单,却是后面故障转移验证的基础——如果连正常状态下数据都不同步,切换后必然丢数据,排查起来更痛苦。

5.4 哨兵视角:如何确认三兄弟都上线了

主从没问题后,随便挑一个哨兵容器查询它视角里的拓扑:

docker exec sentinel-1 redis-cli -p 26379 sentinel master mymaster docker exec sentinel-1 redis-cli -p 26379 sentinel replicas mymaster docker exec sentinel-1 redis-cli -p 26379 sentinel sentinels mymaster

sentinel master mymaster输出里的ip和port字段,就是哨兵眼中当前主库的地址,应该是172.20.0.10和6379。sentinel replicas会列出两个从库,sentinel sentinels会看到另外两个哨兵地址。如果这里显示的数量不对,大概率是哨兵之间的protected-mode没关,或者bind没绑对,导致哨兵之间无法互通。

6. 故障转移演练:手动杀掉Master,看哨兵如何接管

6.1 演练前的配置调整

为了不用盯着日志干等60秒,我把down-after-milliseconds调成了5000毫秒。也就是说,主库断连5秒后,哨兵就会开始判断主观下线。演练开始前,先确认当前主库确实是172.20.0.10。

6.2 模拟主库宕机

直接停掉主库容器:

docker stop redis-master

注意这里用的是stop而不是kill,两者都行,stop更温和一点。主库停掉后,大概几秒钟内,三个哨兵都会陆续在日志里打出sdown标记:

docker logs sentinel-1 2>&1 | tail -50

你会看到类似下面这样的日志序列:

+sdown master mymaster 172.20.0.10 6379 +odown master mymaster 172.20.0.10 6379 #quorum 2/2 +new-epoch 1 +try-failover master mymaster 172.20.0.10 6379 +vote-for-leader 9d539e3... 1 +elected-leader master mymaster 172.20.0.10 6379 +failover-state-select-slave master mymaster 172.20.0.10 6379 +selected-slave slave 172.20.0.11:6379 172.20.0.11 6379 @ mymaster 172.20.0.10 6379 +promoted-slave slave 172.20.0.11:6379 172.20.0.11 6379 @ mymaster 172.20.0.10 6379 +failover-state-reconf-slaves master mymaster 172.20.0.10 6379 +slave-reconf-sent slave 172.20.0.12:6379 172.20.0.12 6379 @ mymaster 172.20.0.10 6379 +slave-reconf-inprog slave 172.20.0.12:6379 172.20.0.12 6379 @ mymaster 172.20.0.10 6379 +slave-reconf-done slave 172.20.0.12:6379 172.20.0.12 6379 @ mymaster 172.20.0.10 6379 +failover-end master mymaster 172.20.0.10 6379 +switch-master mymaster 172.20.0.10 6379 172.20.0.11 6379

这段日志值得一行行读懂。sdown变成odown说明达到了quorum=2;elected-leader说明某个哨兵竞选成功,成为这次故障转移的执行者;selected-slave显示它选中了172.20.0.11作为新主,也就是原来的从库1;switch-master标志着最终完成切换,新主地址已经更新。

6.3 验证新主:谁是新的老大

切换完成后,在任意哨兵上执行:

docker exec sentinel-2 redis-cli -p 26379 sentinel master mymaster

输出的ip字段应该已经变成172.20.0.11。再进新主库确认角色:

docker exec redis-slave-1 redis-cli info replication

可以看到role:master,而且它的从库列表里出现了原来的从库2(172.20.0.12)。原主库172.20.0.10此刻处于离线状态,自然不在列表里。

6.4 旧主恢复:它会自动变成新主的从库

这是我个人觉得哨兵最神奇的一步。把旧主库拉起来:

docker start redis-master

等几秒后,再进去看它的状态:

docker exec redis-master redis-cli info replication

你会发现它已经不再是主库,而是变成了新主(172.20.0.11)的从库:

role:slave master_host:172.20.0.11 master_port:6379

整个过程没有任何人工介入。哨兵在旧主恢复连接后,自动向它发送了replicaof命令,把它归队到新主底下。数据也会从新主同步过来。这种“自动归队”机制是生产环境高可用的关键,很多人在手工管理主从时最容易漏掉这一步。

6.5 客户端视角:接入哨兵而不是直连主库

这次演练暴露了一个重要事实:业务客户端如果要享受故障转移带来的好处,就不能把连接地址硬编码成某个Redis实例的IP,正确的做法是配置哨兵地址。以Spring Boot为例:

spring: redis: sentinel: master: mymaster nodes: sentinel-1:26379,sentinel-2:26379,sentinel-3:26379

客户端启动时会向哨兵询问当前主库地址,之后还会持续监听哨兵通知。主库一旦切换,客户端会自动感知并重新建立连接。如果不这么做,即使哨兵完成了故障转移,你的业务依然在往旧主IP上打数据,高可用等于白搭。

7. 高频踩坑复盘:容器化哨兵的常见意外与排查链路

7.1 坑一:哨兵通告的地址让外部客户端连不上

我一开始把Redis容器映射到宿主机端口(比如6379映射成16379),然后本地用客户端连接,结果发现哨兵返回的主库地址是172.20.0.10:6379,这个IP在宿主机上根本不可达,连接直接失败。

原因在于:哨兵通过INFO命令拿到的主库地址是容器内的IP和端口,它不会知道你做了端口映射。要彻底解决这个问题,最好的方案是让所有需要访问Redis的业务容器与Redis集群置于同一个Docker网络,直接走内部地址。如果你一定要从宿主机外部访问,可以考虑在Linux下使用host网络模式,或者额外配置replica-announce-ip等参数,但这些方案各有局限,远不如网络内互通来得干净。

7.2 坑二:哨兵状态随容器销毁一起蒸发

哨兵会把运行中发现的从库列表、其他哨兵地址、当前epoch等信息写回配置文件,这是它恢复状态的关键。如果像我第一次那样,只挂载了配置文件的初始副本,没有给容器内数据目录做持久化,容器销毁重建后,哨兵会忘掉之前发现的所有从库和哨兵节点,整个集群的感知能力直接归零。

解决办法很简单:像示例compose里那样同时挂载./sentinelN/data:/data,并在sentinel.conf中指定dir /data。这样即使容器重建,哨兵也能从磁盘恢复之前的观察状态。

7.3 坑三:protected-mode没关,哨兵之间互相“失联”

有一个非常隐蔽的排查经历:主从都正常,但sentinel sentinels mymaster始终只返回自己,不显示其他哨兵。我一开始以为是网络问题,后来进入容器里手动ping、telnet都没问题,最后才想起来Redis默认protected-mode yes,非回环地址的请求会被直接拒绝。

哨兵之间的通信、哨兵对主从库的心跳,全部来自非回环IP,所以protected-mode no必须设置。如果出于安全考虑不想全局关闭,也可以同时配置bind和密码认证,但对本地演练环境来说,直接关掉是最省事的。

7.4 坑四:哨兵一直报sdown但迟迟不触发切换

这是我最开始调quorum参数时踩的坑。我把quorum设成了3,但演练时只停了主库,三个哨兵都能检测到主库掉线,按理说应该达成客观下线。结果日志里只有sdown,迟迟不出现odown,更别说后续的switch-master了。

排查链路是这样的:先看哨兵日志,确认是否出现odown;如果没有,说明哨兵之间在“该不该下线”这件事上没有达成一致。检查quorum配置后我发现,虽然三个哨兵都观察到了主库掉线,但quorum=3要求“三个哨兵都确认”,可其中一个哨兵容器因为网络重启过、配置文件丢失,早已不参与投票,实际只有两个有效哨兵,永远凑不齐3票。后来我把quorum调回2,同时修复了那个哨兵的持久化问题,切换才顺利触发。

这里有个经验:quorum的取值不是越大越安全,它要和存活哨兵数量配合。3个哨兵时quorum=2是最合理的,既能容忍一个哨兵故障,又能在主库真掉线时快速达成共识。

7.5 坑五:持久化策略没设计好,切换后丢数据

还有一个容易被忽略的问题:如果主库只开了RDB快照,没有开AOF,故障转移时新主很可能丢数据。因为RDB是周期性快照,最后一次快照之后写入的数据在崩溃时完全丢失。

我的建议是至少在master和slave上都开启appendonly yes,并合理设置appendfsync策略。同时关注Redis本身的主从异步复制机制:切换过程中可能有部分数据尚未同步到新主,这是一种可接受的“最终一致性”折中。如果业务对一致性要求极高,还需要在客户端层面做额外设计,比如配合WAIT命令或把Redis仅当作可丢失部分数据的缓存层来使用。

8. 从搭建完成到真正可用:还有几件小事要记牢

跑完一遍故障转移,这套环境已经能让你对哨兵机制建立起完整的直觉。但如果想把它变成可落地的生产方案,我还会建议你认真对待下面几件事。

8.1 密码认证场景下的额外配置

如果给Redis加了requirepass,主从和哨兵都要增加相应的认证配置。从库要设置masterauth,否则无法复制;哨兵配置里要增加一行sentinel auth-pass mymaster <密码>。漏掉任何一处,故障转移能正常发生,但复制链路会一直报“NOAUTH Authentication required”。

8.2 监控哨兵本身,而不是只监控Redis

哨兵是高可用系统的“大脑”,但它也是单点进程。三个哨兵节点分布在多台宿主机上才能避免物理机故障引发的全挂。另外要给哨兵的日志接入告警,这里不用太复杂,定期扫描日志中的+sdown、+odown、+switch-master关键字就够了。我经历过一次主库切换后,新主持续抖动,日志里反复出现+failover-triggered,如果没有告警,客户端会被反复断连折腾到崩溃。

8.3 客户端配置必须“带名字”连接

哨兵模式下的客户端不是简单连某个IP,而是通过masterName(比如我这里的mymaster)来获取真正的master地址。各种语言的Redis客户端基本都支持Sentinel模式,配置时务必确认选对模式,而不是只把哨兵地址当普通Redis地址来连,否则你用哨兵端口连进去只会看到一堆奇怪的命令异常。

8.4 多次演练之后再上生产

最后分享一点个人体会:不要第一次搭完哨兵就直接搬到生产环境。我在本地环境反复演练过十几次故障转移,才慢慢摸清主库恢复后的归队行为、网络闪断对sdown误判的影响、以及parallel-syncs设置过大对从库全量同步的冲击。每次演练都用真实的业务流量模拟写入,观察切换耗时和数据一致性,把参数调到一个自己心里踏实的范围,再谈上线。高可用系统不是“搭出来”就完了,而是“养出来”的。你越熟悉它在异常情况下的一举一动,真正出事时才越不慌。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询