Redis高可用架构模式详解
目录
- 主从模式 (Master-Slave)
- 哨兵模式 (Sentinel)
- 集群模式 (Cluster)
- 三种模式对比
主从模式
1. 原理概述
主从模式是Redis最基础的高可用方案,通过数据复制实现读写分离和数据备份。
┌─────────────┐ 复制 ┌─────────────┐
│ Master │─────────→ │ Slave 1 │
│ (读写) │ │ (只读) │
└─────────────┘ └─────────────┘│ │ 复制 ▼
┌─────────────┐
│ Slave 2 │
│ (只读) │
└─────────────┘
2. 复制过程详解
2.1 全量复制 (Full Resynchronization)
时序图:
Master Slave│ ││◄─────── PSYNC ? -1 ──────────│ 1. Slave发送同步请求│ ││────── +FULLRESYNC ──────────►│ 2. Master响应全量同步│ runid offset ││ ││────────── RDB ──────────────►│ 3. Master发送RDB快照│ ││─────── 增量数据 ─────────────►│ 4. 发送复制期间的新数据│ │
2.2 增量复制 (Partial Resynchronization)
# Redis 2.8+ 支持增量复制机制
# 基于复制偏移量和复制积压缓冲区# Master维护:
replication_backlog = CircularBuffer(size=1MB) # 复制积压缓冲区
master_repl_offset = 12345 # 主复制偏移量# Slave维护:
slave_repl_offset = 12340 # 从复制偏移量
master_runid = "abc123..." # 主服务器运行ID
3. 配置实现
3.1 Master配置 (redis-master.conf)
# 基本配置
port 6379
bind 0.0.0.0
protected-mode no# 持久化配置
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb# 复制配置
# 设置从服务器密码验证
requirepass "master_password"
# 主服务器密码(如果主服务器也需要认证)
masterauth "master_password"# 复制积压缓冲区大小
repl-backlog-size 1mb
# 复制积压缓冲区超时时间
repl-backlog-ttl 3600# 网络配置
tcp-keepalive 300
timeout 0
3.2 Slave配置 (redis-slave.conf)
# 基本配置
port 6380
bind 0.0.0.0
protected-mode no# 从服务器配置
slaveof 192.168.1.100 6379
# 主服务器密码
masterauth "master_password"
# 从服务器密码
requirepass "slave_password"# 从服务器只读
slave-read-only yes# 复制相关配置
# 当主从连接断开时,从服务器是否继续提供服务
slave-serve-stale-data yes
# 复制超时时间
repl-timeout 60# 复制期间禁用TCP_NODELAY
repl-disable-tcp-nodelay no# 从服务器优先级(用于故障转移)
slave-priority 100
4. 命令操作
# 启动Master
redis-server redis-master.conf# 启动Slave
redis-server redis-slave.conf# 动态设置主从关系
redis-cli -p 6380
127.0.0.1:6380> SLAVEOF 192.168.1.100 6379
OK# 查看复制信息
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.101,port=6380,state=online,offset=12345,lag=0
slave1:ip=192.168.1.102,port=6380,state=online,offset=12345,lag=0# 取消主从关系
127.0.0.1:6380> SLAVEOF NO ONE
OK
5. 主从模式的限制
- 故障转移需要人工干预
- 写操作单点瓶颈
- 主节点故障时服务不可用
哨兵模式
1. 原理概述
哨兵模式在主从模式基础上增加了自动故障转移功能,通过多个哨兵节点监控Redis集群状态。
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Sentinel 1 │ │ Sentinel 2 │ │ Sentinel 3 │
│ (监控) │ │ (监控) │ │ (监控) │
└─────────────┘ └─────────────┘ └─────────────┘│ │ │└───────────────────┼───────────────────┘│ 监控和故障转移▼
┌─────────────┐ 复制 ┌─────────────┐
│ Master │─────────→│ Slave 1 │
│ (读写) │ │ (只读) │
└─────────────┘ └─────────────┘│ │ 复制 ▼
┌─────────────┐
│ Slave 2 │
│ (只读) │
└─────────────┘
2. 哨兵工作机制
2.1 监控 (Monitoring)
# 哨兵定期向Master和Slave发送PING命令
def monitor_redis_instances():for instance in redis_instances:try:response = instance.ping()if response != "PONG":mark_as_down(instance)except Exception:mark_as_down(instance)
2.2 故障检测和判断
# 主观下线 (Subjectively Down, SDOWN)
def check_sdown(instance):if instance.last_ping_time + down_after_milliseconds < current_time():instance.flags |= SRI_S_DOWNreturn Truereturn False# 客观下线 (Objectively Down, ODOWN)
def check_odown(master):sdown_count = 0for sentinel in sentinels:if sentinel.is_master_down(master):sdown_count += 1if sdown_count >= quorum:master.flags |= SRI_O_DOWNreturn Truereturn False
2.3 故障转移流程
故障转移步骤:
1. 检测Master客观下线
2. 选举Leader Sentinel
3. 选择新的Master
4. 执行故障转移
5. 更新配置并通知时序图:
Sentinel1 Sentinel2 Sentinel3 Master Slave1 Slave2│ │ │ │ │ ││─────── PING ──────────────────►│ │ ││ │ │ X │ ││◄─── timeout ──────────────────│ │ ││ │ │ │ ││───── is-master-down-by-addr ──►│ │ ││◄───── +odown ─────────────────│ │ ││ │ │ │ ││──── Leader选举 ──────────────►│ │ ││◄─── Leader确认 ──────────────│ │ ││ │ │ │ ││─────── SLAVEOF NO ONE ─────────────────►│ ││◄────── OK ────────────────────────────│ ││ │ │ │ ││─────── SLAVEOF new_master ─────────────────────►││◄────── OK ──────────────────────────────────────│
3. 配置实现
3.1 哨兵配置 (sentinel.conf)
# 哨兵端口
port 26379
# 绑定地址
bind 0.0.0.0
# 工作目录
dir /var/lib/redis# 监控Master配置
# sentinel monitor <master-name> <ip> <port> <quorum>
sentinel monitor mymaster 192.168.1.100 6379 2# 认证配置
sentinel auth-pass mymaster master_password# 故障转移配置
# 判断Master下线的时间(毫秒)
sentinel down-after-milliseconds mymaster 5000# 故障转移超时时间
sentinel failover-timeout mymaster 15000# 同时从新Master同步数据的Slave数量
sentinel parallel-syncs mymaster 1# 日志配置
logfile "/var/log/redis/sentinel.log"
loglevel notice# 通知脚本(可选)
sentinel notification-script mymaster /scripts/notify.sh
sentinel client-reconfig-script mymaster /scripts/reconfig.sh
3.2 启动哨兵集群
# 启动3个哨兵节点
redis-sentinel /etc/redis/sentinel-26379.conf
redis-sentinel /etc/redis/sentinel-26380.conf
redis-sentinel /etc/redis/sentinel-26381.conf# 或使用systemd
systemctl start redis-sentinel@26379
systemctl start redis-sentinel@26380
systemctl start redis-sentinel@26381
4. 客户端连接
4.1 Java客户端 (Jedis)
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;public class RedisSentinelClient {private JedisSentinelPool sentinelPool;public void initSentinelPool() {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(20);poolConfig.setMaxIdle(10);poolConfig.setMinIdle(5);sentinelPool = new JedisSentinelPool("mymaster", // Master名称sentinels, // 哨兵地址集合poolConfig, // 连接池配置"master_password" // Redis密码);}public void useRedis() {try (Jedis jedis = sentinelPool.getResource()) {jedis.set("key", "value");String value = jedis.get("key");System.out.println("Value: " + value);}}
}
4.2 Python客户端 (redis-py)
import redis.sentinel# 配置哨兵
sentinels = [('192.168.1.100', 26379),('192.168.1.101', 26379),('192.168.1.102', 26379)
]# 创建哨兵实例
sentinel = redis.sentinel.Sentinel(sentinels,socket_timeout=0.1,password='master_password'
)# 获取Master连接
master = sentinel.master_for('mymaster', socket_timeout=0.1)# 获取Slave连接(读操作)
slave = sentinel.slave_for('mymaster', socket_timeout=0.1)# 使用示例
master.set('key', 'value')
value = slave.get('key')
print(f'Value: {value}')
5. 哨兵命令
# 连接哨兵
redis-cli -p 26379# 查看Master信息
127.0.0.1:26379> SENTINEL masters# 查看Slave信息
127.0.0.1:26379> SENTINEL slaves mymaster# 查看哨兵信息
127.0.0.1:26379> SENTINEL sentinels mymaster# 手动故障转移
127.0.0.1:26379> SENTINEL failover mymaster# 重置Master
127.0.0.1:26379> SENTINEL reset mymaster
集群模式
1. 原理概述
Redis Cluster是Redis官方的分布式解决方案,支持数据分片和自动故障转移。
集群拓扑结构:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ (Master) │ │ (Master) │ │ (Master) │
│ Slots: │ │ Slots: │ │ Slots: │
│ 0-5460 │ │ 5461-10922 │ │ 10923-16383 │
└─────────────┘ └─────────────┘ └─────────────┘│ │ ││ │ │
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Node 4 │ │ Node 5 │ │ Node 6 │
│ (Slave of 1)│ │ (Slave of 2)│ │ (Slave of 3)│
└─────────────┘ └─────────────┘ └─────────────┘
2. 数据分片机制
2.1 哈希槽 (Hash Slots)
# Redis Cluster使用16384个哈希槽
CLUSTER_SLOTS = 16384def calculate_slot(key):"""计算key对应的槽位"""# 检查是否有哈希标签 {tag}start = key.find('{')if start != -1:end = key.find('}', start + 1)if end != -1 and end != start + 1:key = key[start + 1:end]# 使用CRC16算法计算槽位crc = crc16(key.encode('utf-8'))return crc % CLUSTER_SLOTS# 示例
print(calculate_slot("user:1000")) # 槽位: 8842
print(calculate_slot("user:{tag}:1000")) # 使用tag计算
print(calculate_slot("user:{tag}:2000")) # 相同tag,相同槽位
2.2 槽位分配
# 集群槽位分配示例
Node 192.168.1.100:7000 - Slots: 0-5460 (5461 slots)
Node 192.168.1.101:7000 - Slots: 5461-10922 (5462 slots)
Node 192.168.1.102:7000 - Slots: 10923-16383 (5461 slots)
3. 配置实现
3.1 集群节点配置 (redis-cluster.conf)
# 基本配置
port 7000
bind 0.0.0.0
protected-mode no# 集群模式启用
cluster-enabled yes
# 集群配置文件(自动生成)
cluster-config-file nodes-7000.conf
# 节点超时时间
cluster-node-timeout 5000
# 集群require-full-coverage
cluster-require-full-coverage no# 数据持久化
appendonly yes
appendfilename "appendonly-7000.aof"# 内存配置
maxmemory 1gb
maxmemory-policy allkeys-lru# 日志配置
logfile "/var/log/redis/redis-7000.log"
loglevel notice# 密码配置(可选)
requirepass "cluster_password"
masterauth "cluster_password"
3.2 创建集群脚本
#!/bin/bash
# create-cluster.sh# 创建6个节点的目录
for port in 7000 7001 7002 7003 7004 7005; domkdir -p /etc/redis/cluster/${port}cp redis-cluster.conf /etc/redis/cluster/${port}/redis.confsed -i "s/port 7000/port ${port}/g" /etc/redis/cluster/${port}/redis.confsed -i "s/nodes-7000.conf/nodes-${port}.conf/g" /etc/redis/cluster/${port}/redis.confsed -i "s/appendonly-7000.aof/appendonly-${port}.aof/g" /etc/redis/cluster/${port}/redis.confsed -i "s/redis-7000.log/redis-${port}.log/g" /etc/redis/cluster/${port}/redis.conf
done# 启动所有节点
for port in 7000 7001 7002 7003 7004 7005; doredis-server /etc/redis/cluster/${port}/redis.conf &
donesleep 5# 创建集群(Redis 5.0+)
redis-cli --cluster create \
192.168.1.100:7000 \
192.168.1.100:7001 \
192.168.1.100:7002 \
192.168.1.100:7003 \
192.168.1.100:7004 \
192.168.1.100:7005 \
--cluster-replicas 1 \
--cluster-yesecho "Cluster created successfully!"
3.3 多机部署配置
# 机器1 (192.168.1.100) - 运行节点 7000, 7003
# 机器2 (192.168.1.101) - 运行节点 7001, 7004
# 机器3 (192.168.1.102) - 运行节点 7002, 7005# 创建集群
redis-cli --cluster create \
192.168.1.100:7000 \
192.168.1.101:7001 \
192.168.1.102:7002 \
192.168.1.100:7003 \
192.168.1.101:7004 \
192.168.1.102:7005 \
--cluster-replicas 1
4. 集群管理
4.1 集群信息查看
# 连接集群(任意节点)
redis-cli -c -h 192.168.1.100 -p 7000# 查看集群信息
127.0.0.1:7000> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3# 查看集群节点
127.0.0.1:7000> CLUSTER NODES
a1b2c3d4... 192.168.1.100:7000 myself,master - 0 0 1 connected 0-5460
e5f6g7h8... 192.168.1.101:7001 master - 0 1623456789 2 connected 5461-10922
i9j0k1l2... 192.168.1.102:7002 master - 0 1623456789 3 connected 10923-16383
m3n4o5p6... 192.168.1.100:7003 slave e5f6g7h8... 0 1623456789 4 connected
q7r8s9t0... 192.168.1.101:7004 slave i9j0k1l2... 0 1623456789 5 connected
u1v2w3x4... 192.168.1.102:7005 slave a1b2c3d4... 0 1623456789 6 connected# 查看槽位分配
127.0.0.1:7000> CLUSTER SLOTS
1) 1) (integer) 02) (integer) 54603) 1) "192.168.1.100"2) (integer) 70003) "a1b2c3d4..."4) 1) "192.168.1.102"2) (integer) 70053) "u1v2w3x4..."
4.2 集群扩容
# 1. 添加新的Master节点
redis-cli --cluster add-node 192.168.1.103:7006 192.168.1.100:7000# 2. 重新分配槽位
redis-cli --cluster reshard 192.168.1.100:7000
# 按提示输入要迁移的槽位数量和目标节点ID# 3. 添加Slave节点
redis-cli --cluster add-node 192.168.1.103:7007 192.168.1.100:7000 --cluster-slave --cluster-master-id <master-node-id>
4.3 集群缩容
# 1. 迁移槽位到其他节点
redis-cli --cluster reshard 192.168.1.100:7000 \
--cluster-from <node-id> \
--cluster-to <target-node-id> \
--cluster-slots 1000# 2. 删除空的Master节点
redis-cli --cluster del-node 192.168.1.103:7006 <node-id># 3. 删除Slave节点
redis-cli --cluster del-node 192.168.1.103:7007 <slave-node-id>
5. 客户端连接
5.1 Java客户端 (Jedis)
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.HostAndPort;public class RedisClusterClient {private JedisCluster jedisCluster;public void initCluster() {Set<HostAndPort> nodes = new HashSet<>();nodes.add(new HostAndPort("192.168.1.100", 7000));nodes.add(new HostAndPort("192.168.1.101", 7001));nodes.add(new HostAndPort("192.168.1.102", 7002));JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(20);poolConfig.setMaxIdle(10);poolConfig.setMinIdle(5);jedisCluster = new JedisCluster(nodes, // 集群节点2000, // 连接超时2000, // 读取超时 5, // 最大重定向次数"cluster_password", // 密码poolConfig // 连接池配置);}public void useCluster() {// 自动处理槽位路由和重定向jedisCluster.set("user:1000", "John");jedisCluster.set("user:1001", "Jane");String user1 = jedisCluster.get("user:1000");String user2 = jedisCluster.get("user:1001");// 批量操作需要在同一槽位Map<String, String> users = new HashMap<>();users.put("user:{group1}:1000", "John");users.put("user:{group1}:1001", "Jane");jedisCluster.mset(users);}
}
5.2 Python客户端 (redis-py-cluster)
from rediscluster import RedisCluster# 配置集群节点
startup_nodes = [{"host": "192.168.1.100", "port": "7000"},{"host": "192.168.1.101", "port": "7001"},{"host": "192.168.1.102", "port": "7002"}
]# 创建集群连接
cluster = RedisCluster(startup_nodes=startup_nodes,decode_responses=True,password="cluster_password",skip_full_coverage_check=True,max_connections=20
)# 使用集群
cluster.set("user:1000", "John")
cluster.set("product:2000", "Laptop")user = cluster.get("user:1000")
product = cluster.get("product:2000")# 使用哈希标签确保相关数据在同一槽位
cluster.mset({"order:{user1000}:detail": "order details","order:{user1000}:items": "order items","order:{user1000}:payment": "payment info"
})
6. 故障转移机制
6.1 自动故障检测
# 集群节点故障检测机制
def detect_node_failure():for node in cluster_nodes:if node.last_ping_time + NODE_TIMEOUT < current_time():node.flags |= REDIS_NODE_PFAIL # 标记为可能失败# 询问其他节点的意见fail_reports = 0for other_node in cluster_nodes:if other_node.get_node_flag(node.id) & REDIS_NODE_PFAIL:fail_reports += 1# 如果大多数节点认为该节点失败if fail_reports > len(cluster_nodes) // 2:node.flags |= REDIS_NODE_FAILtrigger_failover(node)
6.2 Slave提升为Master
# 故障转移过程
1. 检测Master下线
2. Slave节点开始选举
3. 选举出新Master
4. 更新集群配置
5. 重新分配连接# 故障转移日志示例
[WARNING] Node 192.168.1.100:7000 is now possibly failing (PFAIL)
[WARNING] Node 192.168.1.100:7000 is now failing (FAIL)
[NOTICE] Start of election delayed for 500 milliseconds
[NOTICE] Failover election started by 192.168.1.102:7005
[NOTICE] Failover election won: new configuration epoch is 7
[NOTICE] configEpoch set to 7 after successful failover
[NOTICE] Setting secondary replication ID
三种模式对比
1. 功能对比表
| 特性 | 主从模式 | 哨兵模式 | 集群模式 |
|---|---|---|---|
| 数据分片 | ❌ | ❌ | ✅ |
| 自动故障转移 | ❌ | ✅ | ✅ |
| 读写分离 | ✅ | ✅ | ❌ (需手动实现) |
| 横向扩展 | ❌ | ❌ | ✅ |
| 配置复杂度 | 低 | 中 | 高 |
| 客户端支持 | 简单 | 中等 | 复杂 |
| 最小节点数 | 2 | 3+3 | 6 |
2. 性能对比
2.1 写性能
主从模式: 单Master写入,性能受限于单机
哨兵模式: 单Master写入,性能受限于单机
集群模式: 多Master并行写入,性能线性扩展
2.2 读性能
主从模式: Master+Slave读取,可配置读写分离
哨兵模式: Master+Slave读取,可配置读写分离
集群模式: 所有节点都可读取,但需要正确路由
3. 使用场景建议
3.1 主从模式适用场景
- 小型应用,数据量不大
- 读多写少的场景
- 对一致性要求高,可接受手动故障恢复
- 预算有限,硬件资源少
3.2 哨兵模式适用场景
- 中型应用,需要高可用
- 读写比例均衡
- 需要自动故障转移
- 数据量在单机可承受范围内
3.3 集群模式适用场景
- 大型应用,大数据量
- 需要水平扩展
- 写操作频繁
- 可以接受分布式系统的复杂性
4. 迁移路径
4.1 主从 → 哨兵模式
# 1. 停止应用写入
# 2. 部署哨兵节点
# 3. 修改应用连接配置
# 4. 重启应用
# 5. 测试故障转移# 配置变更示例
# 原配置
redis_host = "192.168.1.100"
redis_port = 6379# 新配置
sentinels = [("192.168.1.100", 26379),("192.168.1.101", 26379), ("192.168.1.102", 26379)
]
master_name = "mymaster"
4.2 哨兵 → 集群模式
# 1. 备份现有数据
redis-cli --rdb /backup/dump.rdb# 2. 创建新集群
# 3. 导入数据到集群
redis-cli --cluster import 192.168.1.100:7000 --cluster-from 192.168.1.100:6379# 4. 验证数据完整性
# 5. 切换应用连接
# 6. 下线旧环境
5. 监控和运维
5.1 关键监控指标
# 主从模式监控
- 主从延迟 (replication lag)
- 复制缓冲区使用率
- 主从连接状态# 哨兵模式监控
- 哨兵节点状态
- 故障转移次数
- Master切换历史# 集群模式监控
- 集群节点状态
- 槽位迁移状态
- 集群性能指标
- 数据分布均匀性
5.2 运维脚本示例
#!/bin/bash
# redis-health-check.shcheck_redis_cluster() {echo "=== Redis Cluster Health Check ==="# 检查集群状态cluster_state=$(redis-cli -c -h 192.168.1.100 -p 7000 cluster info | grep "cluster_state" | cut -d: -f2)echo "Cluster State: $cluster_state"# 检查节点状态echo "=== Node Status ==="redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes | while read line; donode_id=$(echo $line | awk '{print $1}')node_addr=$(echo $line | awk '{print $2}')node_flags=$(echo $line | awk '{print $3}')echo "Node: $node_addr, Flags: $node_flags"done# 检查槽位分配echo "=== Slot Distribution ==="redis-cli -c -h 192.168.1.100 -p 7000 cluster slots | grep -E "^\d+" | wc -lecho "Active slot ranges: $(redis-cli -c -h 192.168.1.100 -p 7000 cluster slots | grep -E "^\d+" | wc -l)"
}check_redis_cluster
总结
Redis的三种高可用架构各有特点:
- 主从模式:简单易用,适合小型应用
- 哨兵模式:自动故障转移,适合中型应用
- 集群模式:分布式架构,适合大型应用
选择合适的架构需要考虑:
- 数据量大小
- 并发要求
- 可用性要求
- 运维复杂度
- 硬件成本
建议根据业务发展阶段逐步演进:单机 → 主从 → 哨兵 → 集群
