一、主从模式
1、概述
通过持久化功能,Redis保证了即使在服务器重启的情况下也不会损失(或少量损失)数据。但是由于数据是存储在一台服务器上的,如果这台服务器出现硬盘故障等问题,也会导致数据丢失。为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此,Redis提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。
Redis主从同步策略:主从数据库刚刚连接的时候,采用全量同步来初始化slave节点。salve正常工作时,采用增量同步来将主服务器发生的写操作同步到从服务器。无论如何,redis首先会尝试进行增量同步,如不成功,要求从数据库进行全量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。
-
全量同步:当一个从数据库启动后,会向主数据库发送
SYNC
命令。主数据库接收到SYNC
命令后会开始在后台保存快照(即RDB持久化的过程),并将保存快照期间接收到的命令缓存起来。当快照完成后,Redis会将快照文件和所有缓存的命令发送给从数据库。从数据库收到后,会载入快照文件并执行收到的缓存的命令。 -
增量复制:从数据库发送
PSYNC
命令,格式为PSYNC 主数据库的运行ID 断开前最新的命令偏移量
。主数据库收到PSYNC
命令后,首先会判断从数据库传送来的运行ID是否和自己的运行ID相同(确保从数据库之前确实是和自己同步的,以免从数据库拿到错误的数据),然后判断从数据库最后同步成功的命令偏移量是否在积压队列中。两个条件都满足的话就可以执行增量复制,并将积压队列中相应的命令发送给从数据库。如果此次重连不满足增量复制的条件,主数据库会进行一次全量同步。
缺点:master节点在主从模式中唯一,若master挂掉,则redis无法对外提供写服务。
2、部署配置
主数据库不需要配置,从数据库的配置文件(redis.conf)中加入 slaveof 数据库地址 主数据库端口
即可。
# 检查Redis是否安装
redis-cli --version
# 若未安装,以Ubuntu为例:
sudo apt update
sudo apt install redis-server
# 创建配置文件目录
mkdir -p redis/{master,slave}
# 编辑主节点的 Redis 配置文件
tee redis/master/redis.conf << EOF
port 6379 # 默认端口
bind 0.0.0.0 # 允许远程连接(或指定主节点IP)
protected-mode no # 关闭保护模式(仅测试环境,生产环境需配置密码)
requirepass "your_master_password" # 设置主节点密码(可选)
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "master.log"
dir ./data
EOF
# 配置从节点
tee redis/master/redis.conf << EOF
port 6380
bind 0.0.0.0
protected-mode no
masterauth "your_master_password"
daemonize yes
pidfile /var/run/redis_6380.pid
logfile "slave.log"
dir ./data
replicaof 127.0.0.1 6379
EOF
# 重启Redis 服务
sudo systemctl restart redis-server
sudo systemctl restart redis-server
3、验证主从复制
在主节点写入数据
redis-cli -h <master_ip> -a your_master_password
127.0.0.1:6379> SET key1 "Hello from Master"
OK
在从节点读取数据
redis-cli -h <slave_ip> -p 6380
127.0.0.1:6380> GET key1
"Hello from Master"
检查复制状态
在从节点执行:
127.0.0.1:6380> INFO replication
# 应显示类似:
# role:slave
# master_host:<master_ip>
# master_port:6379
# master_link_status:up
二、哨兵模式
1、概述
Redis 哨兵模式(Sentinel)是 Redis 官方提供的一种高可用性(High Availability, HA)解决方案。它的核心目标是在主从复制架构的基础上,解决主节点(Master)故障时自动进行故障转移(Failover),从而最大限度地减少服务中断时间。
使用sentinel模式的时候,客户端就不要直接连接Redis,而是连接sentinel的ip和port,由sentinel来提供具体的可提供服务的Redis实现,这样当master节点挂掉以后,sentinel就会感知并将新的master节点提供给使用者。
2、架构组成
-
Redis 主节点(Master):提供读写服务。
-
Redis 从节点(Slave/Replica):复制主节点的数据,提供读服务(可选),并作为主节点的候补。
-
Redis 哨兵节点(Sentinel):
-
本质上是一个特殊的 Redis 实例(运行
redis-sentinel
或redis-server --sentinel
)。 -
不存储应用数据,只负责监控和管理 Redis 主从节点。
-
必须部署多个哨兵节点(通常是 3 个或 5 个,且为奇数) 以形成哨兵集群,自身实现高可用和决策仲裁。
-
哨兵节点之间互相通信、互相监控。
-
3、哨兵模式的核心工作流程
- 监控(Monitoring):
- 每个哨兵节点会定期(可配置,如每秒一次)向它配置监控的所有主节点、从节点以及其他哨兵节点发送
PING
命令。 - 根据
PING
的回复(有效回复、无效回复、无回复)和超时时间来判断实例的状态(主观下线)。
- 每个哨兵节点会定期(可配置,如每秒一次)向它配置监控的所有主节点、从节点以及其他哨兵节点发送
- 主观下线(Subjectively Down, SDOWN):
- 如果某个哨兵节点在配置的时间内(
down-after-milliseconds
)没有收到某个 Redis 实例(主、从或其它哨兵)的有效PING
回复,该哨兵会主观地将这个实例标记为“主观下线”。 - 主观下线是单个哨兵的局部判断。
- 如果某个哨兵节点在配置的时间内(
- 客观下线(Objectively Down, ODOWN):
- 当哨兵将一个主节点标记为主观下线后,它会向其他哨兵节点发送
SENTINEL is-master-down-by-addr
命令,询问它们是否也认为该主节点已下线。 - 如果收到足够数量(由配置项
quorum
指定)的哨兵节点确认(它们也认为该主节点主观下线),那么发起询问的哨兵就会将主节点标记为“客观下线”。 - 达到
quorum
值是触发后续自动故障转移的必要条件。 - 客观下线是整个哨兵集群达成共识的判断,表明主节点真的可能挂了。
- 当哨兵将一个主节点标记为主观下线后,它会向其他哨兵节点发送
- 选举领头哨兵(Leader Election):
- 一旦主节点被判定为客观下线,哨兵集群需要选举出一个领头哨兵(Leader Sentinel) 来负责执行本次故障转移操作。
- 选举基于 Raft 算法的变种实现:
- 发现主节点客观下线的哨兵会向其他哨兵发送请求,要求投票选自己为领头哨兵。
- 哨兵遵循“先到先得”和“目标纪元(epoch)更大优先”的原则投票。
- 获得多数票(
> N/2
,N 是哨兵总数)的哨兵成为领头哨兵。 - 如果一轮投票未选出,会在随机延时后开启新一轮投票。
- 故障转移(Failover):
- 被选出的领头哨兵负责执行故障转移:
- 选择新主节点:根据规则(优先级
replica-priority
、复制偏移量、Run ID 等)从旧主节点的从节点中选出一个最合适的作为新的主节点。 - 提升新主节点:向选中的从节点发送
SLAVEOF no one
命令,将其提升为主节点。 - 重新配置其他从节点:向其他从节点发送
SLAVEOF
命令,让它们改为复制新的主节点。 - 更新配置:将旧主节点(如果恢复)更新为新的主节点的从节点。
- 通知客户端:通过发布订阅功能更新配置信息,客户端连接到哨兵时会获取到新的主节点地址。
- 选择新主节点:根据规则(优先级
- 被选出的领头哨兵负责执行故障转移:
- 配置传播(Configuration Propagation):
- 领头哨兵会将故障转移后产生的新配置(新的主节点是谁)广播给其他哨兵节点。
- 所有哨兵节点最终会更新自己的内部配置,记录当前的主节点信息。
4、哨兵模式的优势
- 高可用性:自动故障转移大大减少了主节点故障导致的停机时间。
- 监控与告警:提供对 Redis 实例运行状态的持续监控和通知能力。
- 服务发现:客户端无需硬编码主节点地址,通过连接哨兵即可获取当前有效的主节点地址。
- 官方支持:是 Redis 原生的 HA 方案,兼容性好,社区支持完善。
- 相对简单:相比于 Redis Cluster,部署和配置相对简单,尤其适用于中小规模集群。
5、哨兵模式的局限与注意事项
-
脑裂问题(Split-Brain):
- 在网络分区(Network Partition)的情况下,可能出现两个区域都认为自己是主节点的情况,导致数据不一致。
- 缓解措施:
- 设置
min-replicas-to-write
:主节点必须至少有 N 个从节点连接才允许写入。 - 设置
min-replicas-max-lag
:从节点复制延迟不能超过 M 秒才算有效连接。 - 使用合理的
quorum
值(大于哨兵总数一半)。 - 部署在可靠的网络环境中。
- 设置
-
故障转移时间:从主节点故障到新主节点提供服务,存在一定延迟(秒级到几十秒),期间服务可能短暂不可用。
-
配置复杂性:需要部署和管理多个哨兵实例,配置项较多。
-
不解决数据分片(Sharding):哨兵只负责高可用,不解决单节点数据容量和写性能瓶颈问题。需要横向扩展写能力时,应使用 Redis Cluster。
-
资源消耗:哨兵进程本身需要消耗一定的 CPU 和内存资源。
-
客户端支持:客户端需要支持哨兵协议才能正确地从哨兵获取主节点信息。
6、哨兵模式集群搭建
以下是基于 3 个 Redis 节点(1 主 + 2 从)和 3 个 Sentinel 节点的完整搭建流程(以 Linux 为例):
(1)部署建议
- 哨兵数量:至少 3 个,且部署在独立的、物理隔离的机器或虚拟机上。奇数个(3,5)有利于避免选举僵局。避免将多个哨兵部署在同一台故障域机器上。
quorum
值:通常设置为(哨兵总数 / 2) + 1
(如 3 个哨兵时设为 2)。这个值决定了判断主节点客观下线所需的票数。- 网络:确保 Redis 实例之间、哨兵之间、哨兵与 Redis 实例之间的网络通信稳定、低延迟。
- 持久化配置:确保主节点和从节点都配置了合适的持久化策略(RDB 或 AOF),以保证故障转移后数据不丢失。
- 监控哨兵本身:哨兵节点也需要被监控,防止哨兵集群失效。
(2)环境规划
角色 | IP | 端口 |
---|---|---|
Master | 127.0.0.1 | 6379 |
Replica-1 | 127.0.0.1 | 6380 |
Replica-2 | 127.0.0.1 | 6381 |
Sentinel-1 | 127.0.0.1 | 26379 |
Sentinel-2 | 127.0.0.1 | 26380 |
Sentinel-3 | 127.0.0.1 | 26381 |
生产环境需将节点分散在不同服务器,此处用单机多端口演示
(3)节点配置
# 创建配置文件目录
mkdir -p redis/{master,slave1,slave2} sentinel/{sentinel1,sentinel2,sentinel3}
# 配置 Master(6379)
tee redis/master/redis.conf << EOF
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "master.log"
dir ./data
EOF
# 配置 Replica-1(6380)
tee redis/slave1/redis.conf << EOF
port 6380
daemonize yes
pidfile /var/run/redis_6380.pid
logfile "slave1.log"
dir ./data
replicaof 127.0.0.1 6379 # 指向Master
EOF
# 配置 Replica-2(6381)
tee redis/slave2/redis.conf << EOF
port 6381
daemonize yes
pidfile /var/run/redis_6381.pid
logfile "slave2.log"
dir ./data
replicaof 127.0.0.1 6379 # 指向Master
EOF
# Sentinel-1 配置(26379)
tee redis/sentinel1/redis.conf << EOF
port 26379
daemonize yes
logfile "sentinel1.log"
sentinel monitor mymaster 127.0.0.1 6379 2 # 监控Master,quorum=2
sentinel down-after-milliseconds mymaster 5000 # 5秒无响应判为主观下线
sentinel failover-timeout mymaster 60000 # 故障转移超时60秒
sentinel parallel-syncs mymaster 1 # 同时同步的从节点数
EOF
# Sentinel-2 配置(26379)
cp sentinel/sentinel1/sentinel.conf sentinel/sentinel2/sentinel.conf
sed -i 's/26379/26380/g; s/sentinel1/sentinel2/g' sentinel/sentinel2/sentinel.conf
# Sentinel-3 配置(26379)
cp sentinel/sentinel1/sentinel.conf sentinel/sentinel3/sentinel.conf
sed -i 's/26379/26381/g; s/sentinel1/sentinel3/g' sentinel/sentinel3/sentinel.conf
(4)启动集群
#启动 Redis 主从节点
redis-server redis/master/redis.conf
redis-server redis/slave1/redis.conf
redis-server redis/slave2/redis.conf
# 启动 Sentinel 节点
redis-sentinel sentinel/sentinel1/sentinel.conf
redis-sentinel sentinel/sentinel2/sentinel.conf
redis-sentinel sentinel/sentinel3/sentinel.conf
# 验证主从状态
redis-cli -p 6379 info replication # 查看Master角色和Slaves连接
redis-cli -p 6380 info replication # 确认Slave复制状态
(5)测试故障转移
# 检查 Sentinel 监控状态
redis-cli -p 26379 sentinel master mymaster
# 输出应显示主节点信息及哨兵数量
# 模拟 Master 宕机
kill -9 $(pgrep -f "redis-server.*6379")
# 观察故障转移(约 10-30 秒)
tail -f sentinel/sentinel1/sentinel.log # 搜索 +switch-master 事件
# 检查新 Master
redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
# 输出新Master的IP和端口(如 127.0.0.1 6380)
# 重启旧 Master,它将自动变为新 Master 的从节点
redis-server redis/master/redis.conf
redis-cli -p 6379 info replication # 确认角色变为 slave
(6)客户端连接哨兵集群
使用支持 Sentinel 的客户端(如 Redis 的 redis-py
):
from redis.sentinel import Sentinel
sentinel_list = [('127.0.0.1', 26379), ('127.0.0.1', 26380), ('127.0.0.1', 26381)]
sentinel = Sentinel(sentinel_list, socket_timeout=0.5)
# 获取当前主节点
master = sentinel.discover_master('mymaster')
print("Master:", master)
# 获取从节点列表
slaves = sentinel.discover_slaves('mymaster')
print("Slaves:", slaves)
# 写入操作(自动路由到Master)
master_client = sentinel.master_for('mymaster')
master_client.set('key', 'value')
# 读取操作(可路由到Slave)
slave_client = sentinel.slave_for('mymaster')
print(slave_client.get('key'))
使用sentinel模式的时候,客户端就不要直接连接Redis,而是连接sentinel的ip和port,由sentinel来提供具体的可提供服务的Redis实现,这样当master节点挂掉以后,sentinel就会感知并将新的master节点提供给使用者。
三、集群模式
Redis 集群模式(Redis Cluster)是 Redis 官方提供的分布式解决方案,用于解决单机 Redis 在数据容量和并发性能上的瓶颈。它通过数据分片(Sharding)、高可用(High Availability) 和自动故障转移三大核心能力,实现大规模数据的分布式存储与访问。
Redis 集群模式通过创新的 哈希槽分片 和 去中心化架构,实现了真正意义上的分布式存储。虽然牺牲了部分兼容性(如跨槽位事务),但其水平扩展能力和高可用设计使其成为处理海量数据的首选方案。对于需要突破单机瓶颈的场景(如百万QPS、TB级数据),集群模式是 Redis 生态中最成熟的分布式解决方案。
1、核心设计目标
-
水平扩展
-
数据分散存储在多个节点(分片),突破单机内存/性能限制
-
支持动态增删节点,容量可线性扩展至 1000+ 节点
-
-
高可用
-
主从复制架构 + 自动故障转移(类似哨兵但内置实现)
-
部分节点故障时仍能提供服务
-
-
高性能
- 并行操作不同分片,提升整体吞吐量
-
去中心化架构
- 无代理层,节点间通过 Gossip 协议通信
2、关键架构原理
(1)数据分片机制(Sharding)
- 哈希槽(Hash Slot)
- 整个集群划分为 16384 个固定槽位(0-16383)
- 每个键通过 CRC16 算法计算哈希值,再对 16384 取模确定所属槽位:
slot = CRC16(key) mod 16384
- 槽位分配
- 主节点负责处理特定范围的槽位(如 Node1: 0-5000, Node2: 5001-10000)
- 集群启动时自动分配槽位,支持动态重分配(
CLUSTER ADDSLOTS
)
(2)节点角色
角色 | 数量要求 | 职责 |
---|---|---|
主节点 | ≥ 3 | 处理读写请求,管理槽位 |
从节点 | 每个主节点 ≥1 | 复制主节点数据,故障时接替主节点 |
⚠️ 最小集群配置:3 主节点 + 3 从节点(保证高可用)
(3)节点通信协议
- Gossip 协议
- 节点间周期性交换信息(PING/PONG 消息)
- 携带数据:节点状态、槽位映射、故障标识等
- 特点:低带宽消耗,最终一致性
- 通信端口
- 客户端访问端口(如 6379)
- 集群总线端口(客户端端口 + 10000,如 16379),用于节点间通信
3、工作流程详解
(1)客户端请求路由
- 重定向机制
- 客户端首次连接可能收到
-MOVED
响应,包含目标节点地址 - 智能客户端(如 JedisCluster)会缓存槽位映射表,自动路由请求
- 客户端首次连接可能收到
(2)故障检测与转移
- 主观下线(PFAIL)
- 节点 A 在
cluster-node-timeout
内未收到节点 B 的响应,标记 B 为 PFAIL
- 节点 A 在
- 客观下线(FAIL)
- 节点 A 通过 Gossip 协议广播 B 的 PFAIL 状态
- 当多数主节点确认 B 为 PFAIL 时,触发客观下线
- 从节点选举
- 故障主节点的从节点发起选举(基于 Raft 变种)
- 获胜从节点执行
SLAVEOF NO ONE
升级为主节点
- 槽位接管
- 新主节点广播
PONG
消息通知集群更新拓扑
- 新主节点广播
(3)数据迁移(Resharding)
- 场景:扩容/缩容时重新分配槽位
- 命令:
redis-cli --cluster reshard
- 过程:
- 目标节点准备导入槽位(
CLUSTER SETSLOT <slot> IMPORTING <src-id>
) - 源节点准备导出槽位(
CLUSTER SETSLOT <slot> MIGRATING <dst-id>
) - 批量迁移键值(
MIGRATE
命令原子迁移单个键) - 广播槽位所有权变更
- 目标节点准备导入槽位(
4、核心特性与限制
✅ 优势
特性 | 说明 |
---|---|
自动分片 | 数据均匀分布,无需客户端处理分片逻辑 |
无缝扩缩容 | 支持动态增删节点,数据自动再平衡 |
内置高可用 | 主从切换无需额外组件(如 Sentinel) |
部分故障容忍 | 只要主节点及其从节点不同时宕机,集群仍可用 |
批量操作优化 | 支持跨节点 {tag} 的批量命令(如 MSET ) |
⚠️ 限制与注意事项
限制项 | 说明 |
---|---|
不支持多数据库 | 仅能用 db0,SELECT 命令被禁用 |
跨节点事务受限 | 仅支持相同槽位的键事务(可通过 Hash Tag 强制键同槽位) |
批量操作限制 | 跨槽位 mget/mset 需拆分为单节点操作 |
Lua 脚本约束 | 脚本中所有键必须在同一槽位(EVAL 需用 {tag} ) |
键大小限制 | 单个键值不能超过 512MB |
网络分区风险 | 可能出现脑裂(可通过 min-slaves-to-write 缓解) |
5、集群搭建示例(6 节点)
# 创建节点目录
mkdir -p redis-cluster/{7000..7005}
# 基础配置 (以 7000 为例)
cat > redis-cluster/7000/redis.conf <<EOF
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF
# 复制配置到其他节点
for port in {7001..7005}; do
cp redis-cluster/7000/redis.conf redis-cluster/$port/
sed -i "s/7000/$port/g" redis-cluster/$port/redis.conf
done
# 启动所有节点
for port in {7000..7005}; do
redis-server redis-cluster/$port/redis.conf
done
# 创建集群 (3主3从)
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1 # 每个主节点配1个从节点
6、集群扩容
# 1. 准备新节点配置
mkdir redis-cluster/7006 && cat > redis-cluster/7006/redis.conf <<EOF
port 7006
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF
mkdir redis-cluster/7007 && sed 's/7006/7007/g' redis-cluster/7006/redis.conf > redis-cluster/7007/redis.conf
# 2. 启动新节点
redis-server redis-cluster/7006/redis.conf
redis-server redis-cluster/7007/redis.conf
# 3. 添加主节点到集群
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
# 4. 添加从节点并指定主节点
# 先查询7006的节点ID
redis-cli -p 7006 cluster nodes | grep myself
# 输出示例: d3f6c1f... 127.0.0.1:7006@17006 myself,master - 0 0 0 connected
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 \
--cluster-slave --cluster-master-id d3f6c1f...
# 5. 重新分配槽位 (迁移16384/4≈4096个槽)
redis-cli --cluster reshard 127.0.0.1:7000
# 交互提示:
How many slots do you want to move? 4096
What is the receiving node ID? [输入7006的节点ID]
Source node 1: all
7、集群缩容
# 1. 迁移待删除主节点槽位到其他主节点
redis-cli --cluster reshard 127.0.0.1:7000 \
--cluster-from d3f6c1f... \ # 待删除节点ID
--cluster-to 5f9d1a2... \ # 接收槽位的节点ID
--cluster-slots 1365 \ # 迁移槽位数
--cluster-yes
# 重复直到所有槽位移出 (需执行多次)
# 2. 移除空主节点
redis-cli --cluster del-node 127.0.0.1:7000 d3f6c1f...
# 3. 移除从节点
redis-cli --cluster del-node 127.0.0.1:7000 7007节点ID
8、客户端连接
Maven 依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.4.3</version>
</dependency>
Java 连接代码
import redis.clients.jedis.*;
import redis.clients.jedis.resps.ClusterNode;
import java.util.*;
public class RedisClusterDemo {
private static final int CONN_TIMEOUT = 2000;
private static final int SO_TIMEOUT = 2000;
private static final int MAX_ATTEMPTS = 3;
public static void main(String[] args) {
// 1. 配置集群节点
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("127.0.0.1", 7000));
nodes.add(new HostAndPort("127.0.0.1", 7001));
nodes.add(new HostAndPort("127.0.0.1", 7002));
// 添加所有节点地址...
// 2. 创建集群连接
JedisClusterConfig config = new DefaultJedisClientConfig.Builder()
.connectionTimeoutMs(CONN_TIMEOUT)
.socketTimeoutMs(SO_TIMEOUT)
.build();
try (JedisCluster jedisCluster = new JedisCluster(nodes, config, MAX_ATTEMPTS)) {
// 3. 集群信息检查
System.out.println("=== Cluster Info ===");
System.out.println(jedisCluster.clusterInfo());
// 4. 键操作 (自动路由)
jedisCluster.set("user:1001", "Alice");
System.out.println("GET user:1001: " + jedisCluster.get("user:1001"));
// 5. 使用HashTag确保多键在同一槽位
jedisCluster.set("{order}:1001", "Pending");
jedisCluster.set("{order}:1002", "Shipped");
// 6. Pipeline批量操作 (需相同槽位)
Pipeline pipeline = jedisCluster.pipelined();
pipeline.set("counter", "0");
pipeline.incr("counter");
pipeline.incr("counter");
List<Object> results = pipeline.syncAndReturnAll();
System.out.println("Pipeline results: " + results);
// 7. 直接访问特定节点
Map<String, ConnectionPool> nodesMap = jedisCluster.getClusterNodes();
try (Jedis jedis = nodesMap.get("127.0.0.1:7000").getResource()) {
System.out.println("Node 7000 keys: " + jedis.keys("*"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
9、适用场景 vs 哨兵模式
场景 | 集群模式 | 哨兵模式 |
---|---|---|
数据量 | > 100GB | < 100GB |
写并发 | > 5万 QPS | < 5万 QPS |
扩展需求 | 需水平扩展 | 仅需高可用 |
多键操作 | 需槽位规划 | 无限制 |
运维复杂度 | 较高 | 较低 |
根据 CAP 理论,Redis 集群优先保证 AP(可用性+分区容忍性),通过异步复制牺牲部分一致性。
四、代理模式
代理模式的核心目标是为Redis集群提供统一的入口,解决客户端分片的复杂性,并实现负载均衡、高可用等特性。
1、代理模式核心价值
-
统一入口:为客户端提供单一访问端点
-
协议兼容:支持标准Redis协议,客户端无需修改
-
透明分片:自动处理数据分片逻辑
-
高可用保障:内置故障检测与转移
-
运维简化:节点变更对客户端透明
2、主流代理方案对比
特性 | Twemproxy | Codis | Predixy | Redis Cerberus |
---|---|---|---|---|
开发语言 | C | Go | C++ | C |
动态扩缩容 | ❌ | ✅ | ✅ | ✅ |
数据迁移 | 手动 | 自动 | 依赖后端 | 自动 |
读写分离 | ❌ | ✅ | ✅ | ❌ |
多集群支持 | ❌ | ❌ | ✅ | ❌ |
管理界面 | ❌ | ✅ | ❌ | ❌ |
生产成熟度 | ★★★★☆ | ★★★★★ | ★★★★☆ | ★★★☆☆ |
最新维护 | 停止维护 | 活跃 | 活跃 | 活跃 |
3、Twemproxy (nutcracker)
(1)架构原理
(2)核心特性
-
轻量级代理:单线程事件驱动模型
-
分片算法:
-
一致性哈希(ketama)
-
取模分片(modula)
-
-
故障处理:
-
自动摘除故障节点
-
手动恢复后需重新添加
-
(3)配置示例
alpha:
listen: 127.0.0.1:22121
hash: fnv1a_64
distribution: ketama
auto_eject_hosts: true
server_retry_timeout: 30000
server_failure_limit: 3
servers:
- 127.0.0.1:6379:1
- 127.0.0.1:6380:1
(4)致命缺陷
- 节点变更需重启:扩缩容导致连接中断
- 无数据迁移能力:需手动resharding
- 单点瓶颈:单线程模型限制性能扩展
4、Codis
(1)架构全景
(2)核心组件
-
Codis Proxy:无状态代理,支持水平扩展
-
Codis Dashboard:集群管理中枢
-
Codis Group:Redis实例组(1主N从)
-
Storage:元数据存储(Zookeeper/Etcd)
(3)核心优势
-
动态扩缩容:
# 添加新Group codis-admin --dashboard=ADDR --create-group --gid=3 # 数据迁移 codis-admin --reshard --from=1 --to=3 --slots=100
-
自动故障转移:
- 集成Sentinel监控机制
- 主节点故障自动提升从节点
-
Web管理界面:
- 实时监控集群状态
- 可视化数据迁移
(4)生产实践
# 典型部署架构
4x Codis Proxy(8核16G)
3x Codis Group(1主2从,64G内存)
1x Dashboard + 3x Zookeeper
5、Predixy
(1)架构亮点
(2)核心特性
-
多集群支持:
- 可同时代理标准主从、哨兵、集群模式
-
高性能设计:
- 多线程架构(C++实现)
- 单实例支持15万+ QPS
-
智能读写分离:
# 配置读写规则 ReadCommand { + get + hget + mget # 更多读命令... } WriteCommand { + set + hset + del # 更多写命令... }
(3)配置示例
conf
复制
下载
# 代理多个集群
Proxy {
clusters {
+ cluster1 {
type: cluster
servers: ["127.0.0.1:7000", "127.0.0.1:7001"]
}
+ cluster2 {
type: sentinel
master: mymaster
sentinels: ["127.0.0.1:26379", "127.0.0.1:26380"]
}
}
}
# 连接池优化
WorkerThreads 16
ClientTimeout 300
6、Redis Cerberus(Twitter开源)
(1)创新架构
(2)核心价值
- 高可用代理:
- Active-Active双活架构
- 代理层自身高可用
- 快速故障转移:
- 平均切换时间 < 2秒
- 无需Sentinel参与
- 自动拓扑发现:
- 实时监控后端节点状态
- 客户端零配置变更
(3)性能对比
场景 | 直连Redis | Cerberus | 损耗率 |
---|---|---|---|
SET操作 (QPS) | 120,000 | 110,000 | 8.3% |
GET操作 (QPS) | 130,000 | 118,000 | 9.2% |
故障转移时间 | - | 1.8s | - |
7、选型决策树
8、生产环境建议
-
性能敏感型场景:
- Predixy(C++多线程)
- 部署方案:4核/8G * 3实例
-
大规模动态集群:
-
Codis + 专业运维团队
-
关键配置:
# Codis Proxy配置 max_clients = 10000 session_max_timeout = 30m session_max_bulk_size = 512mb
-
-
金融级高可用:
- Redis Cerberus双活部署
- 网络要求:节点间RTT < 1ms
-
代理层监控关键指标:
- 连接数(client_connections)
- 请求延迟(cmd_latency)
- 错误率(cmd_errors)
- 队列深度(request_queue)
最新趋势:云服务商托管代理(如AWS ElastiCache Proxy)逐渐成为企业首选,减少运维复杂度。
9、总结
场景 | 推荐方案 | 核心优势 |
---|---|---|
传统分片 | Twemproxy | 极简稳定,低延迟 |
弹性伸缩集群 | Codis | 完整生态,可视化运维 |
混合架构 | Predixy | 多协议支持,高性能读写分离 |
零容忍故障 | Redis Cerberus | 亚秒级切换,代理层高可用 |
终极建议:对于新建系统,优先考虑云托管方案或官方Redis Cluster;遗留系统改造根据具体场景选择代理方案。
五、集群&代理模式
Redis 代理模式主要用于实现分片、高可用、负载均衡、协议转换、安全控制等功能,是构建大规模 Redis 应用架构的关键组件。
1、Redis 代理模式分类
(1)客户端分片(Client-side Sharding)
- 原理:客户端库(如 Jedis、Lettuce)直接计算 Key 的分片位置,连接对应的 Redis 节点。
- 优点:无代理层,性能高;架构简单。
- 缺点:
- 业务代码耦合分片逻辑,迁移困难。
- 不支持动态扩缩容(需手动调整分片规则)。
- 多语言支持成本高。
- 典型方案:Redis Cluster 的客户端模式(需支持 Cluster 协议的客户端)。
(2)独立代理(Dedicated Proxy)
- 原理:独立的中间件代理所有请求,负责路由、分片、聚合等。
- 优点:
- 业务代码无感知(透明分片)。
- 支持动态扩缩容、读写分离、故障转移。
- 统一控制入口(安全、监控)。
- 缺点:增加网络跳转,性能略降(约 10-20%)。
- 代表中间件:
- Twemproxy (Nutcracker):Twitter 开源,轻量稳定,不支持动态扩缩容。
- Codis:豌豆荚开源,支持动态扩缩容、Web 管理界面,需依赖 ZooKeeper/Etcd。
- Predixy:支持 Redis Sentinel 和 Cluster,高性能,支持读写分离、多租户。
- mcrouter:Meta 开源,支持 Redis/Memcached,适用于复杂路由场景。
(3)集群感知代理(Cluster-Aware Proxy)
- 原理:代理自动感知 Redis Cluster 的拓扑变化,动态路由请求。
- 优点:兼容 Redis Cluster 协议,无需额外维护分片信息。
- 缺点:依赖 Redis Cluster 本身能力(如扩缩容复杂度)。
- 代表方案:
- Redis Cluster Proxy (官方实验版):提供单入口访问 Cluster。
- Envoy Proxy:通过 Redis 过滤器支持 Cluster 路由。
(4)云服务商代理方案
- 原理:云厂商托管的 Redis 服务内置代理(用户无感知)。
- 优点:开箱即用,自动处理高可用、分片、备份。
- 代表服务:
- AWS ElastiCache (支持 Cluster 模式)
- Google Memorystore
- 阿里云 ApsaraDB for Redis
2、技术选型关键维度
维度 | 选项对比 |
---|---|
动态扩缩容 | Codis、云服务 > Redis Cluster > Twemproxy |
性能 | 客户端分片 > 独立代理(Predixy 性能较优) |
复杂度 | 独立代理 > 客户端分片(需业务集成) |
高可用 | Redis Sentinel + Proxy / Redis Cluster / 云服务 |
协议兼容性 | 大部分代理支持 Redis 5+,Codis/Predixy 对多命令支持更完善 |
监控与管理 | Codis 提供 Dashboard;云服务内置监控;Twemproxy 需自行集成 Prometheus |
多语言支持 | 代理模式天然支持多语言;客户端分片需各语言实现 |
安全 | 代理可统一实现 TLS、ACL、IP 白名单 |
3、选型建议
场景 | 方案 | 优点 |
---|---|---|
中小规模集群,追求简单稳定 | Redis Sentinel + Predixy | 支持自动故障转移、读写分离,Predixy 性能优异且配置简单。 |
大规模分片,需动态扩缩容 | Codis | 成熟的分片方案,支持无缝扩缩容,有可视化管控界面(需维护 ZooKeeper) |
云环境部署 | 云厂商托管服务 | 免运维,自动处理高可用、备份、监控(如 AWS ElastiCache Cluster 模式)。 |
Kubernetes 环境 | Redis Cluster + Envoy Proxy | 通过 Envoy 的 Redis 过滤器实现流量治理,适合 Service Mesh 架构。 |
兼容 Redis Cluster 协议 | 官方 Redis Cluster Proxy(待正式发布) | 未来标准方案,目前可用于测试环境。 |
4、性能优化建议
- 连接池:代理层配置足够连接数(如 Predixy 的
worker_threads
)。 - Pipeline 支持:选择支持 Pipeline 的代理(如 Codis/Predixy 优化了 MGET/MSET)。
- 批处理:避免大 Key 或跨节点事务(代理通常不支持 MULTI 跨分片)。
- 监控指标:关注代理延迟(
proxy_duration_us
)、错误率(cmd_err_count
)。