Redis 主从、哨兵、Cluster 有什么区别?Java 项目高可用架构选型
一、前言
很多 Java 项目一开始用 Redis,都是单机部署。
本地开发时这样配置:
spring:
data:
redis:
host: localhost
port: 6379
测试环境这样用也没什么问题。
但到了生产环境,如果还是单机 Redis,就会有明显风险。
一旦 Redis 宕机,可能会导致:
- 登录态失效
- 缓存大量未命中
- 分布式锁不可用
- 限流失效
- 热点接口直接打到数据库
所以生产环境里,Redis 通常不会单机裸跑,而是会使用:
- 主从复制
- Sentinel 哨兵
- Redis Cluster 集群
很多人知道这些名词,但分不清它们到底解决什么问题。
本文就把 Redis 主从、哨兵、Cluster 的区别和选型讲清楚。
二、单机 Redis 有什么问题?
单机 Redis 最大的问题有两个:
单点故障
容量和性能上限
1. 单点故障
如果 Redis 只有一个节点:
应用 -> Redis 单节点
Redis 一挂,应用就无法访问缓存。
如果业务代码没有降级,可能出现:
大量请求阻塞
数据库压力暴增
接口超时
2. 容量和性能上限
单机 Redis 再强,也受限于:
- 单机内存
- 单机 CPU
- 单机网络
- 单机磁盘
如果数据量越来越大,单节点总会遇到上限。
所以 Redis 高可用和扩展就非常重要。
三、主从复制
1. 什么是主从复制?
主从复制就是一个 master 节点负责写,多个 slave 节点复制 master 的数据。
结构大概是:
-> slave1
master -> slave2
-> slave3
master 接收写请求,然后把数据同步给 slave。
slave 可以用于:
- 数据备份
- 读请求分担
- 故障切换基础
2. 主从复制解决什么问题?
主从复制主要解决两个问题:
读扩展
数据副本
如果读请求很多,可以让部分读请求访问从节点。
如果 master 挂了,从节点上还有数据副本。
3. 主从复制不能解决什么?
主从复制本身不能自动故障转移。
也就是说:
master 挂了,slave 不会自动变成 master。
如果没有其他机制,需要人工处理:
选一个 slave
提升为 master
修改客户端连接地址
恢复服务
这显然不适合生产环境。
所以还需要哨兵。
4. 主从复制怎么开启?
Redis 开启主从复制并不复杂,核心就是让从节点执行:
replicaof <master-ip> <master-port>
以前老版本 Redis 里也能看到 slaveof,新版本更推荐使用 replicaof。
假设我们准备 3 个 Redis 节点:
master:10.0.0.10:6379
slave1:10.0.0.11:6379
slave2:10.0.0.12:6379
最简单的方式,是登录从节点执行命令。
在 slave1 上执行:
redis-cli -h 10.0.0.11 -p 6379
127.0.0.1:6379> replicaof 10.0.0.10 6379
OK
在 slave2 上执行:
redis-cli -h 10.0.0.12 -p 6379
127.0.0.1:6379> replicaof 10.0.0.10 6379
OK
这样两个从节点就会开始复制 master 的数据。
不过命令行方式有一个问题:Redis 重启后配置可能丢失。生产环境更推荐写到 redis.conf。
从节点 redis.conf 示例:
port 6379
bind 0.0.0.0
daemonize yes
# 当前节点作为从节点,复制 10.0.0.10:6379
replicaof 10.0.0.10 6379
如果 master 设置了密码,还要在从节点配置 masterauth:
requirepass redis123
masterauth redis123
replicaof 10.0.0.10 6379
这里容易搞混:
requirepass:别人访问当前 Redis 节点需要的密码;masterauth:当前从节点连接 master 时使用的密码。
如果 master 有密码,但从节点没有配置 masterauth,复制会失败。
5. Docker 环境怎么模拟主从?
如果只是学习或测试,可以在本机用 Docker 快速启动 3 个 Redis。
先创建一个网络:
docker network create redis-net
启动 master:
docker run -d --name redis-master \
--network redis-net \
-p 6379:6379 \
redis:7 redis-server --appendonly yes
启动两个 slave:
docker run -d --name redis-slave-1 \
--network redis-net \
-p 6380:6379 \
redis:7 redis-server --replicaof redis-master 6379 --appendonly yes
docker run -d --name redis-slave-2 \
--network redis-net \
-p 6381:6379 \
redis:7 redis-server --replicaof redis-master 6379 --appendonly yes
验证复制是否正常:
docker exec -it redis-master redis-cli
127.0.0.1:6379> set name luobin
OK
再到从节点读取:
docker exec -it redis-slave-1 redis-cli
127.0.0.1:6379> get name
"luobin"
能读到 master 写入的数据,就说明主从复制已经生效。
6. 如何查看主从状态?
Redis 提供了 info replication 命令。
在 master 上执行:
redis-cli -h 10.0.0.10 -p 6379 info replication
你会看到类似信息:
role:master
connected_slaves:2
slave0:ip=10.0.0.11,port=6379,state=online,offset=1024,lag=0
slave1:ip=10.0.0.12,port=6379,state=online,offset=1024,lag=0
master_repl_offset:1024
关键看几个字段:
role:master:当前节点是主节点;connected_slaves:已连接从节点数量;state=online:从节点在线;lag:从节点延迟,越大说明复制越慢;master_repl_offset:master 当前复制偏移量。
在 slave 上执行:
redis-cli -h 10.0.0.11 -p 6379 info replication
会看到类似:
role:slave
master_host:10.0.0.10
master_port:6379
master_link_status:up
slave_read_repl_offset:1024
slave_repl_offset:1024
重点看:
role:slave:当前节点是从节点;master_link_status:up:和 master 连接正常;slave_repl_offset:从节点同步到的偏移量。
如果 master_link_status:down,说明从节点没有连上 master,要检查网络、防火墙、密码和 replicaof 配置。
7. 主从复制的几个生产细节
第一,从节点默认只读。
Redis 从节点默认配置通常是:
replica-read-only yes
这表示从节点不允许写入。不要为了方便把它改成 no,否则从节点写入的数据不会同步回 master,故障切换后很容易出现脏数据。
第二,第一次同步可能比较重。
从节点第一次连接 master 时,通常会触发全量同步。master 会生成 RDB 快照并发送给从节点,从节点加载完成后再继续增量同步。
如果 Redis 数据量很大,第一次挂从库要避开业务高峰,并关注:
- master CPU;
- 网络带宽;
- RDB 生成耗时;
- 从节点加载耗时;
repl-backlog-size是否足够。
第三,主从不是强一致。
写入 master 成功,不代表 slave 已经同步完成。读写分离时要接受短暂旧数据。如果业务刚写完必须马上读到最新值,就应该读 master,或者业务层做一致性兜底。
第四,主从只是哨兵和 Cluster 的基础。
主从复制本身只负责复制数据,不负责自动选主。master 挂了以后,如果没有 Sentinel 或 Cluster,客户端不会自动切到 slave。
四、复制延迟问题
Redis 主从复制是异步的。
也就是说,master 写入成功后,slave 可能还没来得及同步。
看这个场景:
1. 用户更新个人信息
2. 写入 master 成功
3. 立刻从 slave 查询
4. slave 还没同步完成,读到旧数据
这就是复制延迟。
所以读写分离时要注意:
刚写完就要立刻读最新数据,最好读 master。
不是所有读请求都适合走从库。
五、Sentinel 哨兵
1. 哨兵解决什么问题?
Sentinel 主要解决 Redis 主从架构下的自动故障转移问题。
它会做几件事:
- 监控 master 和 slave 是否存活
- master 故障时自动选举新的 master
- 通知客户端新的 master 地址
- 维护 Redis 主从拓扑
结构大概是:
Sentinel 集群
|
监控 Redis 主从
2. 为什么哨兵也要集群?
如果只有一个哨兵,那哨兵自己也会成为单点。
所以生产环境通常部署多个哨兵:
sentinel1
sentinel2
sentinel3
多个哨兵通过投票判断 master 是否真的故障。
这可以避免单个哨兵误判。
3. 哨兵故障切换过程
大致流程如下:
1. 哨兵发现 master 不可用
2. 多个哨兵确认 master 下线
3. 从 slave 中选出一个新的 master
4. 其他 slave 改为复制新 master
5. 客户端获取新的 master 地址
应用侧如果使用支持 Sentinel 的客户端,就能自动感知 master 变化。
六、Spring Boot 连接 Sentinel 示例
配置示例:
spring:
data:
redis:
sentinel:
master: mymaster
nodes:
- 10.0.0.1:26379
- 10.0.0.2:26379
- 10.0.0.3:26379
timeout: 2s
这里的 master 不是 Redis 节点地址,而是哨兵配置中的 master 名称。
Java 客户端会通过哨兵获取当前真正的 master 地址。
所以不要在代码里写死 Redis master IP。
这里要特别注意一个容易误解的点:
Spring Boot 的 sentinel 配置,是让 Java 应用连接哨兵。
Redis 主从复制配置,是让 slave 节点复制 master。
这两件事不是一回事。
也就是说,下面这段配置:
spring:
data:
redis:
sentinel:
master: mymaster
nodes:
- 10.0.0.1:26379
- 10.0.0.2:26379
- 10.0.0.3:26379
timeout: 2s
只能告诉 Java 客户端:
去连接这几个 Sentinel 节点,
然后问它们 mymaster 当前真正的 master Redis 是谁。
它不会自动帮你把 Redis 配成主从,也不会自动启动 Sentinel。
完整顺序应该是:
1. 先部署 Redis master
2. 再部署 Redis slave,并配置 replicaof
3. 再部署 Sentinel,让 Sentinel 监控这个 master
4. 最后 Spring Boot 配置 sentinel 节点,让应用通过 Sentinel 找 master
Sentinel 自己也要有配置文件,比如 sentinel.conf:
port 26379
daemonize yes
sentinel monitor mymaster 10.0.0.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
如果 Redis master 有密码,还需要加:
sentinel auth-pass mymaster redis123
其中:
mymaster要和 Spring Boot 里的sentinel.master保持一致;10.0.0.10 6379是当前 Redis master 的地址;- 最后的
2表示至少 2 个 Sentinel 认为 master 下线,才会触发客观下线判断。
所以 Spring Boot 配置的是“客户端入口”,Redis/Sentinel 配置的是“服务端高可用架构”。两边都配好,Java 项目才能真正具备 Redis 主从自动故障转移能力。
七、Sentinel 适合什么场景?
Sentinel 适合:
- 数据量不算特别大
- 单个 master 能扛住写入
- 主要诉求是高可用
- 不想引入太复杂的分片架构
比如大多数中小型 Java 项目:
Redis 用作缓存、登录态、限流、分布式锁
数据量几十 GB 以内
写入压力不极端
这种情况下,主从 + 哨兵通常就够了。
八、Redis Cluster
1. Cluster 解决什么问题?
Redis Cluster 主要解决的是水平扩展问题。
它把数据分散到多个 master 节点上。
Redis Cluster 使用 16384 个 hash slot。
每个 key 会根据 hash 结果落到某个 slot。
slot 再分配到不同 master 节点。
大致结构:
master1 负责一部分 slot
master2 负责一部分 slot
master3 负责一部分 slot
每个 master 通常还有自己的 slave。
2. Cluster 的优点
Cluster 的优点是:
- 支持数据分片
- 支持水平扩展
- 单节点内存压力降低
- 部分节点故障可以自动切换
当单机 Redis 容量不够时,Cluster 是常见选择。
3. Cluster 的限制
Cluster 也有一些限制。
比如多 key 操作。
如果执行:
mget user:1 order:1
这两个 key 如果不在同一个 slot,可能会报错。
因为 Redis Cluster 不支持跨 slot 的普通多 key 操作。
如果确实要让多个 key 落到同一个 slot,可以使用 hash tag:
user:{1001}:name
order:{1001}:list
{1001} 中的内容会用于计算 slot。
九、Spring Boot 连接 Cluster 示例
spring:
data:
redis:
cluster:
nodes:
- 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
timeout: 2s
使用 Cluster 时,客户端要支持重定向。
因为请求某个 key 时,如果连接的节点不是该 key 所在 slot 的节点,Redis 会返回 MOVED 或 ASK,让客户端跳转到正确节点。
所以一定要使用支持 Cluster 的客户端。
比如:
- Lettuce
- Jedis
- Redisson
十、Sentinel 和 Cluster 怎么选?
可以简单这样判断。
1. 只需要高可用
选择:
主从 + Sentinel
适合大多数普通业务。
优点:
- 架构简单
- 运维成本低
- 客户端使用简单
缺点:
- 写入仍然集中在一个 master
- 单 master 容量有限
2. 需要水平扩展
选择:
Redis Cluster
适合:
- 数据量很大
- 单机内存不够
- 写入吞吐很高
- 需要分片扩展
缺点:
- 架构复杂
- 运维成本更高
- 多 key 操作受限制
- key 设计要更谨慎
十一、故障切换不是完全无感
很多人以为用了哨兵或 Cluster,Redis 故障切换就完全无感。
这不现实。
故障切换期间可能出现:
- 短暂连接失败
- 命令执行超时
- 客户端重连
- 部分写入失败
- 主从数据短暂不一致
所以 Java 业务代码仍然要设置:
- 连接超时
- 读取超时
- 失败重试
- 熔断降级
- 兜底逻辑
不要让 Redis 故障把 Tomcat 线程全部卡死。
十二、Java 项目中的建议
1. 不要无限等待 Redis
Redis 客户端必须设置超时。
比如:
spring:
data:
redis:
timeout: 2s
如果没有超时,Redis 抖动时可能拖垮整个应用。
2. 缓存失败要允许降级
缓存读取失败时,可以考虑直接查数据库。
但要注意防止数据库被打爆。
可以配合:
- 本地缓存
- 限流
- 熔断
- 默认值
3. 锁失败要有业务策略
如果 Redis 分布式锁不可用,业务要明确:
是直接失败?
还是走数据库唯一约束兜底?
还是进入排队?
不要让异常逻辑随机发生。
4. 监控必须到位
至少要关注:
- Redis 连接数
- 内存使用率
- QPS
- 慢查询
- 主从延迟
- key 过期数量
- 故障切换次数
- 客户端超时数量
十三、常见坑
1. 以为主从能自动切换
主从复制本身不负责自动故障转移。
需要 Sentinel 或 Cluster。
2. 读写分离读到旧数据
主从复制有延迟。
刚写完就要读最新数据时,不要随便读 slave。
3. Cluster 中跨 slot 操作失败
多 key 操作要注意 key 是否在同一个 slot。
必要时使用 hash tag。
4. 客户端写死 Redis 地址
使用哨兵或 Cluster 时,客户端必须支持拓扑感知。
不要在代码里写死单个 master。
5. 没有降级
Redis 再高可用,也可能短暂不可用。
业务必须有兜底策略。
十四、总结
Redis 主从、哨兵、Cluster 解决的问题不一样。
简单总结:
- 主从复制:解决数据副本和读扩展
- Sentinel:解决主从架构的自动故障转移
- Cluster:解决数据分片和水平扩展
选型建议:
中小型项目:主从 + Sentinel
大容量高吞吐项目:Redis Cluster
但不管用哪种架构,都不要忘记:
- 客户端超时
- 故障降级
- 数据兜底
- 监控告警
高可用不是部署几个节点就结束了。
真正的高可用,是 Redis 架构、客户端配置、业务降级和监控体系一起配合。
更多推荐
所有评论(0)