单实例 Redis 的两个短板
Redis 性能极高,但单实例有两个绕不开的短板:容量受单机内存限制、单点故障意味着缓存整体不可用。数据涨到几十个 G、或读并发冲到几万时,一台机器既存不下也扛不住;更要命的是,这台机器一旦宕机,所有依赖缓存的接口全部回源数据库,可能直接把库压垮。
解决思路分两步:先解决“挂了怎么办”,用主从加哨兵做高可用;再解决“太大怎么办”,用 Cluster 把数据分片到多台机器。理解了这两层,Redis 的生产部署才算入门。
- 主从复制写主库、读从库,分担读压力并做热备。
- 哨兵独立进程监控主从,主库挂了自动把从库提为主库。
- Cluster把数据按槽分散到多个主节点,水平扩容。
- 槽位16384 个哈希槽,key 落在哪个槽就路由到哪个节点。
主从复制:读写分离
最简单的高可用形态是一主多从:主库负责写,从库实时同步主库的数据、负责读。复制的原理是主库把写操作以命令流的形式持续发给从库,从库重放这些命令,保持数据一致。读多写少的场景(缓存、排行榜)用读写分离,读压力就被从库摊掉了。
但主从只是“数据副本”,主库挂了并不会自动有从库顶上——客户端还在往已经死掉的主库写。这时候要么人工介入切主,要么引入哨兵自动完成。另外复制是异步的,主库写成功就返回,还没同步到从库就宕机,会丢一小段数据,对一致性要求极高的场景要心里有数。
哨兵 Sentinel:自动故障切换
哨兵是一组独立运行的监控进程,它做三件事:定期心跳探测主库和从库是否存活、多数哨兵都认为主库下线时触发客观下线、从从库里挑一个最合适的提升为新主库并让其余从库改跟新主。
要点:哨兵本身也要至少部署 3 个奇数节点,形成多数派投票,否则单个哨兵误判就会乱切主。客户端通过哨兵发现当前的主库地址,主从切换后哨兵会通知客户端新主库在哪。这样主库宕机时,几十秒内自动完成切换,业务基本无感。
为什么还要分片
主从加哨兵解决了高可用,但所有数据还是只存在主库这一台机器的内存里。数据量超过单机内存、或写并发超过单机网卡与 CPU 能力时,再多加从库也没用——写还是只能落在主库。这时候需要的是把数据本身切开,让不同的 key 落在不同的主节点上,这就是分片。
Redis 官方的分片方案就是 Redis Cluster。它把“分片 + 高可用”合在一起:每个主节点负责一部分数据,每个主节点再挂一个从节点做备份,主节点挂了它的从节点自动顶上,不再需要独立的哨兵进程。
Cluster 与 16384 个槽
Redis Cluster 没有用取模,而是引入了哈希槽这个中间层:总共 16384 个槽,每个 key 用 CRC16(key) mod 16384 算出自己该落在哪个槽,再由集群把这些槽平均分配给各个主节点。比如 3 个主节点,就分别负责 0 到 5460、5461 到 10922、10923 到 16383 这三段槽。
# key 与槽的对应:16384 个槽,按节点分段
slot = CRC16("user:1001") mod 16384
# 节点 A 管 0..5460,节点 B 管 5461..10922,节点 C 管 10923..16383
为什么是槽而不是直接对节点取模?因为槽是可以迁移的。扩容加节点时,不用像取模那样把所有 key 重算分布,只需要把某些槽从老节点挪到新节点即可,老 key 的归属在迁移过程中平滑变化。这是 Cluster 能在线扩容的关键。
槽位路由与重定向
客户端连进集群时,会拿到一份槽位与节点的映射表,本地记录“哪个槽在哪个节点”。请求一个 key 时,客户端自己算槽、找节点、直发过去。如果槽已经迁走、或客户端的映射表过期了,节点会返回一个 MOVED 重定向,告诉客户端这个槽现在在哪个节点,客户端据此更新本地映射再重试。
要注意多键操作的限制:同一个事务、Lua 脚本、mget 里的多个 key 必须落在同一个槽,否则集群报错。需要让多个相关 key 同槽时,用哈希标签 {user:1001}:profile 这种花括号语法——只对花括号内的部分算槽,这样同一用户的多个 key 就被绑在同一槽。
扩容、缩容与迁移
Cluster 扩容是把新节点加入集群,然后手工或用工具(如 redis-cli)把一部分槽从老节点迁移到新节点。迁移槽的过程是:先告诉老节点这个槽要迁出、新节点要迁入,然后逐个把该槽下的 key 搬到新节点,搬完再切换槽的归属。这个过程在线进行,对业务基本透明。
运维上要避免几个坑:别把主从放在同一台物理机;每个主节点都要有至少一个从节点兜底;槽分配要尽量均匀,避免某个节点数据和 QPS 明显偏高;客户端要用支持 Cluster 的客户端库,自己处理 MOVED 重定向,别直连单点。
实战清单
落地清单:
- 数据没超过单机内存、并发不高时,主从加哨兵就够。
- 哨兵至少 3 个奇数节点,靠多数派投票避免误切。
- 数据量或写并发超过单机能力时,上 Redis Cluster。
- 理解 16384 个哈希槽,扩容是迁槽不是重算。
- 多键操作要同槽,用哈希标签绑定相关 key。
- 每个主节点配从节点,主从别同机,槽尽量分均。
- 客户端用 Cluster 客户端,自动处理重定向。
Redis 的高可用演进是一条清晰的路:单实例扛不住,先上主从读写分离;主库单点怕挂,再加哨兵自动切主;数据再大就用 Cluster 把槽分片到多机。把主从、哨兵、槽位路由这三层理解透,Redis 从缓存玩具变成生产级存储才真正落地。