
Redis 高可用架构这事儿说简单也简单说复杂能写成一本书。我刚入行那会儿以为 Redis 挂了就挂了重启大法好直到有一天凌晨三点线上订单服务被一个缓存雪崩打趴才发现单机 Redis 就是颗定时炸弹。后来几年陆续折腾过哨兵、摸过集群又在各种故障演练里被脑裂、误判、槽位迁移坑过好几轮才算把这块彻底理顺。这篇内容我想从“为什么需要高可用”“哨兵和集群各自解决了什么”“到底怎么落地”三个层面展开把原理、参数、部署、排错一次性讲透。适合正在用 Redis 但还没上高可用方案的同学也适合已经搭了哨兵或集群但各种报错、failover 玩不溜的运维和后端。我尽量用大白话讲原理再给可直接抄的配置和命令保证你看完能上手也能在面试跟人聊明白。1. 高可用架构到底在解决什么问题1.1 单机 Redis 的“命门”单机 Redis 最大的问题不是慢而是“一个人扛下所有”。读写都指着这一台实例内存满了要淘汰、磁盘满了要清理、进程崩了要人肉重启。最尴尬的是就算你把 Redis 部署得漂漂亮亮机器停电、网络抖动、物理机宕机这种小概率事件一旦发生缓存层直接失联后面的数据库就要瞬间扛住天量流量分分钟被拖死。有人会说那 Redis 支持持久化重启恢复就行。这话没错但关键词是“重启恢复”——恢复需要时间而线上流量可不会等你把 RDB 加载完。AOF 的话恢复更慢几十 GB 的数据 replay 一遍业务早就超时一片了。再说了单机实例连最基本的“故障转移”都做不了它挂了之后所有读写都失败根本没有第二条路可走。所以单机方案只适合三类场景本地开发、纯缓存且允许丢失、数据量小且不追求 SLA。线上核心链路单机就是拿命在赌。1.2 主从复制为什么要加“哨兵”很多团队的第一步是从单机升级到主从复制一个 master 负责写多个 replica 负责读。这样 master 挂了至少 slave 上还有一份全量数据可以手动切过去。听起来比单机稳多了对吧但实际用起来会发现一个痛点主库宕机后如果你不干预整个系统依然不可写。因为客户端和主库之间的连接断了写请求全失败你总不能每次故障都拉个人起来改配置、改 IP、重启服务。Redis 需要一种机制能在 Master 下线时自动把某个 Slave 提拔成新 Master并且让客户端无感地连接到新节点。这个机制就是哨兵。哨兵的职责可以概括成三件事监控主从节点是否存活、在主节点故障时自动执行故障转移、把新的主节点信息通知给客户端。它相当于给 Redis 配了一个 7×24 小时的运维值班员自己判断谁挂了、谁顶上然后广播地址变更。1.3 哨兵还是集群一张表讲清选型逻辑这两个词经常被混着聊但定位完全不同。哨兵解决的是“可用性问题”集群解决的是“容量和性能问题”两者甚至能叠着用。搞不清这点架构很容易做拧巴。维度哨兵模式集群模式核心目标高可用自动故障转移数据分片 高可用数据存储所有节点存全量数据按槽位分散存储容量上限受单机内存限制理论上可水平扩展写扩展性只能写 Master所有主节点都能写故障转移哨兵自动选举集群内部自动选举适用规模几十 GB 以内读多写少数据量大、写并发高复杂度较低较高客户端需支持集群协议简而言之如果你主要痛点是“怕 Redis 挂”选哨兵如果你 Redis 内存已经几十上百 GB、写并发也上来单实例撑不住了那就上集群。现实中也有生产环境用“集群模式 每组分片节点挂哨兵”的极端玩法但日常不建议这么折腾集群自身已经内置了故障转移能力。2. 哨兵机制原理、参数与故障转移全拆解2.1 三个动作与两类下线的判定先明确一个认知哨兵本身也是一个 Redis 实例只是不做数据存储专跑监控逻辑。生产环境至少要部署三个哨兵而不是一个原因后面会细说。哨兵的工作可以拆成三块监控每隔一定周期默认 1 秒向 Master 和 Slave 发送 PING 命令检查它们有没有响应。通知当某个被监控的 Redis 实例出问题时哨兵之间通过发布订阅Pub/Sub机制互相通报状态。自动故障转移当 Master 被判定客观下线后哨兵集群会协商选出一个 Leader由它执行 Slave 提升、配置改写和客户端通知。重点来了下线判定分两种。主观下线Subjectively Down简称 SDOWN单个哨兵发现自己 PING 不通 Master就会把这个实例标记为主观下线。为什么叫“主观”因为可能只是这个哨兵和 Master 之间的网络闪断了别的哨兵还能正常连通。客观下线Objectively Down简称 ODOWN当一个哨兵认为 Master 主观下线后它会通过sentinel is-master-down-by-addr命令询问其他哨兵。如果收到的确认数量达到 quorum法定人数比如 3 个哨兵里 2 个都同意那这个 Master 才被判定为客观下线才会触发故障转移。这里有一个新手常踩的坑quorum 并不是越大越好。quorum3 意味着 3 个哨兵都同意才判定下线如果网络分成了两半可能两边都凑不出多数票反而让故障转移迟迟无法触发。生产环境通常是 3 个哨兵配 quorum25 个哨兵配 quorum3取“过半即可”的原则。2.2 哨兵的核心参数详解附推荐配置哨兵的配置方式是在sentinel.conf里写规则也可以运行时通过命令动态调整。下面这套配置是我在线上验证过的直接抄作业没问题。# sentinel.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 yourpassword sentinel resolve-hostnames no逐条解释一下关键参数sentinel monitor name ip port quorum监控哪个主库、多少个哨兵同意才判定客观下线。name 是逻辑名可以自己起但要保证所有哨兵一致。down-after-milliseconds连续多久没响应 PING 就算主观下线。设 5000 意味着 5 秒内无响应就标记。这个值别设太小否则网络抖动会造成频繁切换也别太大否则真宕机时要等半天才恢复。5 秒是我用过比较稳的平衡值。failover-timeout故障转移的超时时间包括选主、切换、晋升等步骤的总超时。15 秒一般够用。如果网络慢可以放大到 20 秒。parallel-syncs故障转移后同时向新 Master 发起全量同步的从库数量。设 1 表示一个接一个来避免多个 Slave 同时复制导致新主库 CPU 和内存飙高。auth-pass如果 Redis 开了密码哨兵也需要配置密码才能完成主从身份识别。还有两个容易忽略的点。第一所有哨兵的配置文件里monitor 指向的 IP 必须写 Master 节点地址而不是某个哨兵自己的地址。第二哨兵发现 Master 切换后会自动重写自身的配置文件把 Master 地址更新为新的节点所以你千万别把 sentinel.conf 设置成只读否则会报错。2.3 故障转移的完整流程与脑裂防护我会用一次真实的故障演练把整个过程串起来。假设有三个节点Master A10.0.0.1:6379Slave B10.0.0.2:6379Slave C10.0.0.3:6379三个哨兵分别部署在三台机器上操作步骤手动 kill 掉 Master A 的 Redis 进程。发生的事情如下三个哨兵在 1 秒内都发现 PING A 没响应各自在本地标记 SDOWN。它们互相用sentinel is-master-down-by-addr命令确认3 个里至少 2 个同意Master A 升级为 ODOWN。哨兵们开始选举 Leader。这里用的是类似 Raft 的机制每个哨兵都有投票权谁先发起投票、谁获得多数票谁就是 Leader。只有 Leader 才有权限执行故障转移。Leader 在存活 Slave 里挑选新 Master。选举规则优先级是配置优先级slave-priority 越小越优先→ 数据最完整复制偏移量最大→ 运行 ID 最小。选定新 Master假设是 BLeader 向 B 发送slaveof no one让 B 变成独立的 Master。Leader 通知其他 SlaveC执行slaveof B让它们重新指向新主库。所有哨兵更新自己的配置把 Master 指向 B并通过 Pub/Sub 对外发布 switch-master 事件。客户端如果实现了哨兵协议会接这个事件自动刷新连接地址。整个过程看起来挺顺畅但我必须提醒你一个容易被忽视的风险脑裂。假设 Master A 和哨兵们之间的网络断了但 A 本身还活着。哨兵们判定 A 客观下线后把 B 提拔为 Master此时 A 并不知情它还在继续接受写请求。A 的连接恢复后A 会发现自己变成了别人的 Slave于是主动将自己的数据同步给 B——但同步是单向的A 在断连期间产生的写入数据会被 B 的全量数据覆盖掉这些数据直接丢失。解决脑裂的办法不是让哨兵更聪明而是从写入侧做限制。Redis 官方提供了两个配置来保护一致性min-replicas-to-write 1 min-replicas-max-lag 10意思是最少有 1 个从库与主库的复制延迟在 10 秒以内主库才接受写入。如果脑裂导致从库全部失联主库的写入条件不满足它就会拒绝写入从根源上避免断连期间的脏数据产生。这两个参数我在生产环境强制开启宁可损失短暂可用性也不接受不可追踪的数据丢失。注意如果你对数据一致性极度敏感连min-replicas都不能完全信任。Redis 的复制本身是异步的任何宕机切换方案都可能丢几条最新的写请求。高可用和高一致在 Redis 里天然存在取舍别指望它替代传统数据库的事务能力。3. 集群机制分片、通信与选主到底怎么运转3.1 为什么是 16384 个哈希槽聊到 Redis Cluster几乎所有人都会问为什么数据分片偏偏用 16384 个槽而不是 1024、65536网上有各种说法我按自己的理解给你讲一遍。Cluster 不是把 key 直接哈希到节点而是先把 key 做 CRC16 校验然后取模 16384 得到一个槽位编号。每个主节点负责一段连续的槽位范围。比如三主集群节点 A 管 0–5460节点 B 管 5461–10922节点 C 管 10923–16383。为什么是 16384官方给出的解释有几个16384 个槽位在节点数较少时每个节点负责的槽位足够大数据能均匀分布。槽位信息需要定期在节点之间通过 Gossip 协议传播槽位数量太多会导致心跳包变大浪费带宽。比起 6553616384 的心跳包载荷更小。16384 在特定网络包大小下有更好的压缩表现。但其实更直观的理解是16384 是一个在“数据分散粒度”和“元数据传播开销”之间折中的结果。节点才几十个槽位搞几万个没意义槽位太少又容易出现数据倾斜。实际工作中你要关心的不是 16384 这个数字本身而是一件事Redis Cluster 要求一个 key 只能属于一个槽所以跨槽位的多 key 操作比如 mget 跨节点、事务跨节点默认不支持。解决手段是使用哈希标签hash tag把 key 写成{user1001}.profile和{user1001}.cart的形式Redis 只会对花括号内的内容做 CRC16这样两个 key 就能落到同一个槽位从而支持事务和批量操作。这是集群下做业务设计最核心的技巧之一。3.2 Gossip 协议与集群总线集群节点之间互通的链路叫集群总线Cluster Bus用的是另一个端口默认是节点端口 10000。比如 Redis 监听 6379总线就是 16379。很多人排查集群网络问题时容易忽略这个端口结果 Redis 的数据端口通了、总线端口被防火墙挡住节点之间始终没法完成握手和心跳折腾半天才发现是少开放了一个端口。集群里的节点是怎么互相感知的靠的是 Gossip 协议。每个节点每 100ms 会向部分随机节点发送 PING附带自己知道的其他节点状态收到消息的节点会更新自己的集群元数据并继续向其他节点扩散。这种“病毒式传播”的好处是节点数量多时依然能较快收敛且没有中心节点任何一个节点挂了都不影响元数据传播。但 Gossip 也带来了一个隐患节点状态的变化不是瞬间全局同步的会有毫秒级的传播延迟。所以 Cluster 的故障判定也设计了主观/客观两套逻辑节点 A 向节点 B 发 PING 后超过cluster-node-timeout默认 15 秒没收到 PONGA 就认为 B 主观下线并在集群里广播。当集群中超过一半的主节点都认为 B 主观下线B 就被标记为客观下线。此时如果 B 是主节点集群会触发主从切换。这里有个重要的细节只有持有 B 的从节点的节点才有资格发起故障转移投票而整个集群中每个主节点只有一票。选举逻辑简而言之就是“少数服从多数 发起方从拥有从节点的节点开始拉票”和哨兵的 Leader 选举类似但作用范围是整个集群。3.3 集群故障转移与槽位迁移Cluster 的故障转移和哨兵不同的地方在于不需要外部哨兵进程主从切换完全由集群内部完成。假设某个主节点挂了它下面的从节点会先等一段时间计算公式和cluster-node-timeout有关然后向集群里其他主节点发起投票申请。拿到超过半数的票后从节点就会把自己晋升为主节点并接管原主节点负责的槽位。从节点晋升后它广播自己“我变成主节点了”。其他节点收到消息后会把路由表里对应槽位的 owner 更新掉。原来的挂掉主节点即便后来恢复了重新加入集群时也会发现自己已经有主了于是自动降级成从节点。这套机制在多数场景下是自动的不需要人工干预。真正需要人工参与的是“槽位迁移”。比如你要给集群扩容加一个主节点进来不迁移槽位的话新节点就是个光杆司令没有任何数据。集群的扩容公式和步骤大致如下新节点通过cluster meet命令加入集群。用redis-cli --cluster reshard ip:port发起重新分片。输入要迁移的槽位数量比如 4096再输入接收槽位的节点 ID。选择源节点可以是所有节点分担也可以指定某个节点全部迁出。集群会逐个槽位搬运数据搬运过程中 key 还留在旧节点客户端访问该槽位时旧节点看到 key 正在迁移会返回ASK重定向让客户端去新节点查找。整个过程可以热执行不用停机但会占带宽和 CPU。我的经验是大集群迁移槽位前先量一下 key 数量如果某个槽位的 key 特别多迁移时间会非常久最好拆到低峰期做。迁移途中不要手动改集群配置容易把元数据搞乱。4. 从零搭建哨兵与集群的实操记录4.1 三节点哨兵模式部署实战哨兵部署我觉得最顺手的方案是三台机器每台机器上既放一个 Redis 实例又放一个哨兵这样既省机器又能保证哨兵和 Redis 进程的相互独立进程是独立的崩溃不会互相影响。假设三台机器 IP 分别是 10.0.0.1、10.0.0.2、10.0.0.3。第一步先在 10.0.0.1 上装好 Redis配置 redis.conf 关键项bind 0.0.0.0 port 6379 daemonize yes dir /var/lib/redis requirepass redispass masterauth redispass appendonly yesmasterauth特别重要。主从复制和故障转移后Slave 连新 Master 认证需要这个密码。很多人只配 requirepass结果从库一直同步报错NOAUTH Authentication required。第二步在 10.0.0.2 和 10.0.0.3 上同样安装 Redis并在 redis.conf 里加一行replicaof 10.0.0.1 6379然后各自启动确认info replication里master_link_status:up。第三步三台机器上分别写一份 sentinel.confport 26379 daemonize yes 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 redispass启动方式redis-sentinel /etc/redis/sentinel.conf启动后可以用一条命令看哨兵是否已经发现所有节点redis-cli -p 26379 sentinel master mymaster redis-cli -p 26379 sentinel replicas mymaster redis-cli -p 26379 sentinel sentinels mymaster如果输出里出现了三个哨兵的 IP说明集群关系已经建立。然后你可以直接kill -9杀掉 10.0.0.1 的 Redis 主进程等 10 秒左右再查哨兵的 master 是谁你会发现主节点已经切到另一台机器了。切换完成后老主库的配置文件里被写入replicaof 新主库IP它恢复了也会自动变成从库。整个过程完全无人工介入这就是哨兵的价值。注意哨兵模式下客户端连接不能只用原本地地址。你要使用支持哨兵的客户端比如 Java 的 Lettuce 的RedisSentinel模式配置哨兵节点地址和 master 名称。否则主库 IP 变化后客户端依然连旧地址照样故障。4.2 三主三从集群搭建实战集群的搭建比哨兵直观但步骤略多。最稳的方案是六台机器或容器三个主节点负责写三个从节点负责冗余。每台机器的 redis.conf 都要开启集群模式port 6379 daemonize yes dir /var/lib/redis appendonly yes cluster-enabled yes cluster-config-file nodes-6379.conf cluster-node-timeout 15000 requirepass redispass masterauth redispass注意cluster-enabled yes这行是灵魂配置。没有它Redis 不会启动集群协议。cluster-config-file会自动生成不用手动建它会记录当前节点的集群状态。六个节点全部启动后开始建集群。旧版是redis-trib.rb新版直接内置在 redis-cli 里用起来更简单redis-cli -a redispass --cluster create \ 10.0.0.1:6379 10.0.0.2:6379 10.0.0.3:6379 \ 10.0.0.4:6379 10.0.0.5:6379 10.0.0.6:6379 \ --cluster-replicas 1--cluster-replicas 1表示每个主节点配一个从节点所以前三个是主节点后三个自动成为它们的副本。执行过程会先计算槽位分配方案然后问你是否同意输入 yes 就开始跑。跑完可以验证redis-cli -a redispass -c -h 10.0.0.1 -p 6379 cluster info看到cluster_state:ok就是健康状态。-c是 cluster 模式客户端会自动处理 MOVED 重定向这个参数很重要。再用cluster nodes查看节点角色和槽位分布。设置一个测试 keyredis-cli -a redispass -c -p 6379 set foo bar如果该 key 的槽位不在 10.0.0.1 上cluster 模式会自动跳转写入并返回你实际写入的节点。这就是集群“自动路由”的验证。4.3 日常监控与读写分离配置高可用不是搭完就完事日常监控必须跟上。我常用的监控工具是 Prometheus redis_exporter Grafana。redis_exporter 能采集同步延迟、内存、命中率、集群槽位迁移状态、哨兵主节点变化等指标。关键是这几个监控项别漏主从复制延迟master_repl_offset和slave_repl_offset的差值超过阈值要告警。集群节点数变化cluster_known_nodes减少说明有节点掉线。拒绝连接数rejected_connections飙高说明 maxclients 配置不足。哨兵切换事件监听switch-master日志切换次数多了说明网络环境不稳定。读写分离配置方面哨兵模式和集群模式做法不同。哨兵模式下客户端可以用 Lettuce 的 RedisSentinel 配置一组读写分离策略读请求分发到从节点写请求只走 Master。但要注意从节点有最多几秒的复制延迟强一致性读业务别走从节点。集群模式下官方客户端Lettuce、Jedis Cluster、redis-py-cluster默认是读写都走主节点的没做读写分离。你要复用从节点的话可以用客户端的readFrom策略Lettuce 支持REPLICA或者在业务侧自己按 key 做路由。不建议在集群里强行做读写分离因为延迟收益不大复杂度倒是会上去不少。5. 高可用落地中的常见问题与排查实录5.1 误判下线、脑裂与数据丢失我在真实环境里遇到过两类让运维最头疼的问题误判和脑裂。误判的场景通常是这样的某个大 key 在执行慢操作比如KEYS *扫描或超大集合的SUNIONSTORERedis 主线程被卡住好几秒期间哨兵的 PING 一直没得到响应于是被判主观下线。接着多个哨兵确认后触发客观下线主从切换。实际上老主库没死只是“忙到没空理你”。切换过去后老主库恢复正常当上了从库然后全量同步把一份新数据拉过来看起来没啥但那段卡顿时间里的写请求可能已经丢了。这类问题排查思路分三步查是否真的宕机看 Redis 日志有没有 OOM、致命的 segfault看系统负载和 CPU 是否被打满。查是否有慢命令用SLOWLOG GET看最近的慢查询。调整误判阈值down-after-milliseconds从 5000 调到 10000给主从切换留出缓冲。脑裂我前面已经在哨兵部分详细说了核心防护是开启min-replicas-to-write。集群模式下也有类似的隐患。比如某个主节点和集群内其他节点网络隔离它还在持续接收客户端写入其他节点等cluster-node-timeout过后就会把它的从节点提升为新主节点。等网络恢复老主节点发现自己被“降级”会自动丢弃自己那些没有被复制走的写入数据。集群模式下没有类似min-replicas-to-write的全局保护参数所以最靠谱的方案还是业务侧做好幂等或者必要的时候在接入层做降级限流。数据丢失的另一种常见来源是持久化配置不对。高可用架构虽然能帮你切换节点但切换后的新主节点靠的是复制数据。如果你连appendonly yes都没开或者用了默认的 RDB 策略极端情况下数据可能根本没写到磁盘。我的建议是高可用模式下必须开 AOF并且配置appendfsync everysec。这个配置能够在性能和持久化之间取得一个比较平衡的状态最多丢一秒数据对于大多数业务是可以接受的。always性能损耗太大no又太不安全。5.2 集群槽位、网络与客户端问题集群排查中槽位问题是重灾区。见过不止一次有人连集群中的某个节点执行redis-cli -h 10.0.0.2 -p 6379 get foo得到一个MOVED 6275 10.0.0.5:6379的错误。这不是故障而是集群的正常行为——你的请求打到了不负责该槽位的节点上。解决方案就是加-c参数或者让客户端使用集群模式连接。还有一种常见坑是槽位指派不完整。比如你用了cluster replicate手动设主从却忘了给主节点分配槽位结果cluster info显示cluster_state:fail因为当前集群存在未覆盖的槽位。排查方法redis-cli -p 6379 cluster nodes | awk {print $9} | grep \\-这会列出所有没有被分配的槽位区间。如果确实有用cluster addslots逐个补上或者直接用--cluster fix自动修复。槽位迁移卡住也是高频问题。症状是cluster_state:ok但某些 key 访问时一直报ASK重定向过去又找不到数据。大概率是迁移中间态没走完。排查命令redis-cli -p 6379 cluster slots找到状态异常的槽位后重新执行redis-cli --cluster fix来修复迁移状态。注意如果迁移的 key 数量特别大槽位迁移耗时十几分钟是正常的不要一看到ASK就慌着手动干预。网络层面集群对端口的依赖比哨兵更苛刻。开防火墙时除了 6379 这个数据端口还一定要放通 16379 这个集群总线端口。我遇到过最隐蔽的一次故障6379 通了16379 被安全组沉默拒绝节点之间 PING 丢包严重集群状态在ok和fail之间反复横跳客户端疯狂报CLUSTERDOWN。当时排查了很久才发现是这个端口被拒了。客户端这块最容易出问题的是“连接池里的旧路由”。虽然客户端有集群拓扑刷新机制但刷新是有周期的。如果你的连接池缓存了旧节点扩容或摘节点后可能会出现短时间的连接异常。解决办法是在低峰期执行扩容并且把客户端的拓扑刷新时间调小比如 Lettuce 的cluster topology refresh周期默认是 1 分钟线上改成 10 秒会更敏感。5.3 缓存治理与分布式锁的高可用边界聊高可用就不能只谈机器层面业务用的缓存治理和分布式锁也是高可用架构的一部分。很多团队把 Redis 高可用搭好了但业务代码写得非常粗糙一样会出大事。先说缓存治理三个高频词你肯定听过穿透、击穿、雪崩。穿透查询一个不存在的 key请求直接打到数据库。防护手段是布隆过滤器或者在空结果上也设置短 TTL 缓存。击穿某个热点 key 在过期瞬间有大量请求同时打到数据库。防护手段是互斥锁重建缓存或者把过期时间做成逻辑过期即业务值里存一个过期标记后台异步刷新。雪崩大量 key 在同一时间过期或者 Redis 整体挂了。防护手段是过期时间加随机抖动以及在上层做限流降级。我在实际项目里最认可的“缓存高可用治理”顺序是先保证 Redis 本身的可用性哨兵/集群再在业务代码里实现多级缓存本地缓存 Redis DB最后再加限流和降级。三层配合才能扛住真正的高并发。再说分布式锁。Redis 分布式锁最经典的实现是 SETNX EXPIRE 的原子操作核心命令SET lock_key request_id NX PX 30000必须用 SET 命令的 NX 和 PX 一起传保证加锁和过期在一个原子操作里。千万不要先 SETNX 成功再单独执行 EXPIRE这两个步骤之间进程崩溃会导致锁永远不释放。锁的释放也要求校验值。释放时需要比对 value 是否还是自己的 request_id然后Lua脚本原子删除if redis.call(get,KEYS[1]) ARGV[1] then return redis.call(del,KEYS[1]) else return 0 end很多人写释放锁代码的时候直接 DEL结果把别人的锁删了这在并发高的场景下会造成严重的互斥失效。说到高可用分布式锁的安全争议集中在 Redlock 算法官方提出的用多个独立 Redis 实例组锁的方案。我的看法是普通业务用单主 Redis 哨兵模式做加锁是够用的如果你们的业务真的非常依赖分布式锁的强一致语义用 Redlock 需要额外部署多个 Redis 实例且要接受它实现复杂、性能损耗大的现实。真要较真更好的方案是转到 ZooKeeper 或 etcd 这类带线性一致读写的组件上去。别把 Redis 当万能锁用它更适合做“高性能非强一致”的互斥控制。最后再分享一个小技巧。我在压测哨兵切换时不只看 Redis 是否恢复还会盯“切换耗时”这个指标。从哨兵判定 ODOWN 到客户端连接新 Master 完成整个过程最好控制在 30 秒以内。超出这个时间就要查是不是parallel-syncs配置过大、慢查询阻塞了新主库的复制或者客户端对 switch-master 事件的处理逻辑有问题。高可用这事不是“能切就行”而是“切换得够快、够稳、不漏数据”。这套标准才是你在团队里真正值钱的经验。