Redis哨兵Sentinel高可用 Redis 的五种数据结构各有特色,用对了才能发挥它的优势。很多人只用到了 String 和 Hash,却不知道 List、Set、ZSet 在特定场景下更合适。本文从应用场景出发,讲什么时候用什么类型。
一、Sentinel架构 #
1.1 基本架构 ┌─────────┐ ┌─────────┐ ┌─────────┐ │Sentinel1│───>│Sentinel2│───>│Sentinel3│ │ :26379 │ │ :26379 │ │ :26379 │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ └──────────────┼──────────────┘ │ ┌─────┴─────┐ ▼ ▼ ┌─────────┐ ┌─────────┐ │ Master │ │ Slave │ │ :6379 │ │ :6379 │ └─────────┘ └─────────┘
#
1.2 Sentinel的作用
监控 :持续检查Master和Slave是否正常运行
通知 :通过API向管理员或其他应用发送通知
自动故障转移 :Master故障时,自动将Slave提升为Master
配置提供者 :为客户端提供当前Master地址
二、Sentinel配置 #
2.1 Sentinel节点配置 # sentinel.conf # 端口 port 26379 # Sentinel工作目录 dir /var/lib/redis/sentinel # 监控的Master # sentinel monitor <master-name> <ip> <port> <quorum> sentinel monitor mymaster 192.168.1.100 6379 2 # quorum: 判断Master失效需要多少个Sentinel同意 # 如quorum=2,至少2个Sentinel认为Master下线才进行故障转移 # Master密码(如果Master有密码) sentinel auth-pass mymaster password # 判断Master下线的时间(毫秒) sentinel down-after-milliseconds mymaster 5000 # 并行同步的Slave数量 sentinel parallel-syncs mymaster 1 # 故障转移超时时间 sentinel failover-timeout mymaster 60000 # 通知脚本(可选) sentinel notification-script mymaster /var/redis/notify.sh # 故障转移后执行的脚本(可选) sentinel client-reconfig-script mymaster /var/redis/failover.sh
#
2.2 启动Sentinel redis-sentinel /etc/redis/sentinel.conf redis-server /etc/redis/sentinel.conf --sentinel
#
2.3 最少Sentinel数量 推荐至少3个Sentinel :
1个:无法判断客观下线(自己说自己)
2个:如果网络分区,可能出现脑裂
3个:quorum=2,可以安全地进行故障转移
三、故障检测机制 #
3.1 主观下线(SDOWN) 每个Sentinel独立判断: Sentinel1 ──PING──> Master │ ├── 回复+PONG → 正常 ├── 回复-LOADING → 正常(正在加载) ├── 回复-MASTERDOWN → 正常(Master知晓故障) └── 未回复/回复错误 → 主观下线 判断条件: - 超过down-after-milliseconds未收到有效回复
#
3.2 客观下线(ODOWN) Sentinel1发现Master主观下线 │ ├── 向其他Sentinel发送: │ SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid> │ ├── Sentinel2回复:1(同意) ├── Sentinel3回复:1(同意) │ └── 同意数 >= quorum → 客观下线 注意: - 需要包括Sentinel自己 - 如quorum=2,需要Sentinel自己+另一个Sentinel同意
#
3.3 检测命令 PING SENTINEL is-master-down-by-addr 192.168.1.100 6379 0 *
四、故障转移流程 #
4.1 选举Leader Sentinel Sentinel发现Master客观下线 │ ├── 发送SENTINEL is-master-down-by-addr请求 │ (带上自己的runid,请求成为Leader) │ ├── 其他Sentinel回复: │ 如果当前epoch更大 → 拒绝 │ 如果已投票给其他Sentinel → 拒绝 │ 否则 → 同意 │ └── 获得多数票(> Sentinel总数/2)→ 成为Leader 示例:3个Sentinel - Sentinel1请求成为Leader - Sentinel2同意 - Sentinel3同意 - Sentinel1获得2票(> 3/2 = 1.5),成为Leader
#
4.2 选择新Master Leader Sentinel选择新Master: 1. 筛选条件: - Slave必须在线 - Slave的down-after-milliseconds内有过回复 2. 排序规则(优先级从高到低): a. replica-priority最小(配置值) b. 复制偏移量最大(数据最新) c. RunID最小(字典序)
#
4.3 执行故障转移 1. 对新Master执行:SLAVEOF NO ONE → 提升为Master 2. 对其他Slave执行:SLAVEOF new_master_ip port → 重新指向新Master 3. 更新Sentinel配置 → 修改sentinel monitor指向新Master 4. 通知客户端(通过Pub/Sub) +switch-master mymaster old_ip old_port new_ip new_port
#
4.4 故障转移示例 初始状态: Master: 192.168.1.100:6379 Slave: 192.168.1.101:6379 Slave: 192.168.1.102:6379 Master宕机 │ ├── Sentinel检测到客观下线 │ ├── 选举Leader Sentinel │ ├── 选择192.168.1.101为新Master │ (因为复制偏移量最大) │ ├── 对192.168.1.101:SLAVEOF NO ONE │ ├── 对192.168.1.102:SLAVEOF 192.168.1.101 6379 │ └── 通知客户端切换 最终状态: Master: 192.168.1.101:6379 Slave: 192.168.1.102:6379
五、客户端连接 #
5.1 Jedis Sentinel @Configuration public class RedisSentinelConfig { @Bean public JedisSentinelPool jedisSentinelPool () { Set<String> sentinels = new HashSet <>(); sentinels.add("192.168.1.100:26379" ); sentinels.add("192.168.1.101:26379" ); sentinels.add("192.168.1.102:26379" ); JedisPoolConfig poolConfig = new JedisPoolConfig (); poolConfig.setMaxTotal(100 ); poolConfig.setMaxIdle(20 ); return new JedisSentinelPool ("mymaster" , sentinels, poolConfig, "password" ); } } @Autowired private JedisSentinelPool pool;public void setValue (String key, String value) { try (Jedis jedis = pool.getResource()) { jedis.set(key, value); } }
#
5.2 Lettuce Sentinel @Configuration public class RedisConfig { @Bean public RedisConnectionFactory redisConnectionFactory () { RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration () .master("mymaster" ) .sentinel("192.168.1.100" , 26379 ) .sentinel("192.168.1.101" , 26379 ) .sentinel("192.168.1.102" , 26379 ); sentinelConfig.setPassword("password" ); return new LettuceConnectionFactory (sentinelConfig); } }
#
5.3 Spring Boot配置 spring: redis: sentinel: master: mymaster nodes: - 192.168 .1 .100 :26379 - 192.168 .1 .101 :26379 - 192.168 .1 .102 :26379 password: password lettuce: pool: max-active: 100 max-idle: 20 min-idle: 5
六、Sentinel管理命令 #
6.1 查看状态 redis-cli -p 26379 SENTINEL masters redis-cli -p 26379 SENTINEL slaves mymaster redis-cli -p 26379 SENTINEL sentinels mymaster redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster redis-cli -p 26379 SENTINEL failover-abort mymaster
#
6.2 手动故障转移 redis-cli -p 26379 SENTINEL failover mymaster redis-cli -p 26379 SENTINEL remove mymaster redis-cli -p 26379 SENTINEL monitor mymaster 192.168.1.100 6379 2
七、常见问题 #
7.1 脑裂问题 现象 :网络分区导致出现两个Master
网络分区前: Sentinel1 ── Sentinel2 ── Sentinel3 │ │ │ Master ───── Slave1 ──── Slave2 网络分区后: 分区A: 分区B: Sentinel1 Sentinel2 ── Sentinel3 │ │ │ Master Slave1 ───── Slave2 (继续服务) (提升为Master) 结果:两个Master同时服务,数据不一致!
解决 :
# 配置最小Slave数 min-replicas-to-write 1 min-replicas-max-lag 10 # 这样Master如果没有Slave连接,会停止写入
#
7.2 故障转移后客户端连接失败 原因 :
客户端缓存了旧的Master地址
Sentinel通知有延迟
解决 :
#
7.3 故障转移频繁触发 排查 :
tail -f /var/log/redis/sentinel.logredis-cli -p 26379 SENTINEL master mymaster
#
7.4 Sentinel配置不生效 ls -la /etc/redis/sentinel.confredis-cli -p 26379 SENTINEL flush-config
八、Sentinel监控 #
8.1 监控脚本 #!/bin/bash for sentinel in sentinel1 sentinel2 sentinel3; do status=$(redis-cli -h $sentinel -p 26379 PING) if [ "$status " != "PONG" ]; then echo "Sentinel $sentinel 异常" | mail -s "Sentinel告警" admin@company.com fi done master_info=$(redis-cli -h sentinel1 -p 26379 SENTINEL master mymaster) flags=$(echo "$master_info " | grep "flags" | head -1) if echo "$flags " | grep -q "down" ; then echo "Master mymaster 状态异常: $flags " | mail -s "Redis Master告警" admin@company.com fi
#
8.2 Prometheus监控 scrape_configs: - job_name: 'redis-sentinel' static_configs: - targets: ['sentinel1:26379' , 'sentinel2:26379' , 'sentinel3:26379' ]
九、总结
概念
说明
SDOWN
主观下线,单个Sentinel的判断
ODOWN
客观下线,多个Sentinel达成共识
quorum
判断ODOWN所需的最小同意数
epoch
配置纪元,用于Leader选举和配置版本
Leader选举
Raft算法变种,获得多数票的Sentinel成为Leader
配置项
推荐值
说明
quorum
Sentinel数/2 + 1
确保多数同意
down-after-milliseconds
5000
根据网络质量调整
parallel-syncs
1-3
同时同步的Slave数
failover-timeout
60000
故障转移超时
Sentinel的核心价值:
自动故障检测(主观下线→客观下线)
自动故障转移(选举→切换→通知)
客户端透明(自动获取最新Master)
多Sentinel保证决策可靠性
Sentinel的局限:
只有一个Master写(不能水平扩展写)
需要至少3个Sentinel保证可靠性
脑裂问题需要额外配置解决
故障转移期间有短暂不可用
核心要点
String:简单的键值对,适合缓存、计数器
Hash:存储对象属性,适合用户信息、配置
List:有序列表,适合消息队列、最新列表
Set:无序去重,适合共同好友、抽奖
ZSet:有序集合,适合排行榜、积分系统
总结 选择合适的数据结构是使用 Redis 的关键。在实际项目中,根据业务需求选择合适的类型,可以提升性能和开发效率。