Redis 是一个高性能的键值存储系统,广泛应用于缓存、消息队列等场景。为了提高 Redis 的可用性和扩展性,Redis 提供了三种主要的高可用方案:主从模式、哨兵模式和集群模式。本文将详细介绍这三种模式的工作原理、配置方法和适用场景。
1. Redis 主从模式(Master-Slave)
1.1 原理介绍
主从模式是 Redis 最基础的高可用方案,通过读写分离和数据备份提高系统的可用性和读取吞吐量。
核心特点:
- 读写分离:主节点(Master)处理写请求,从节点(Slave)处理读请求
- 数据备份:从节点实时同步主节点数据,实现数据冗余
- 一主多从:一个主节点可以配置多个从节点
1.2 主从复制实现机制
复制ID和偏移量
- replication ID:主库的 replication ID 就是自身 runid,是Redis服务启动时自动生成的40位随机字符串;从库只负责记录并保存主库的 replication ID,自身 runid 不参与主从同步协商
- 复制偏移量(offset):主库每执行一个写命令,会将命令追加到复制积压缓冲区,同时复制偏移量(offset)递增;从库同步执行完写命令后,也会更新自己的复制偏移量,用于标识已同步的数据位置
- 从库连接携带信息:从库建立或重连主库时,会携带已记录的主库 replication ID 和自身已同步的复制偏移量 offset;主库通过比对 replication ID 是否匹配、offset 是否在复制积压缓冲区范围内,判断执行全量同步还是增量同步
SYNC vs PSYNC
| 命令 | 说明 |
|---|---|
| SYNC | 全量同步,无论从节点之前是否同步过,每次都会重新生成 RDB 快照并传输全部数据(Redis 2.8 之前的旧机制) |
| PSYNC | 支持部分重同步,通过复制偏移量和备份 ID 判断,仅传输断线期间的增量数据(Redis 2.8+ 默认方式) |
全量同步(同步)
- 从服务器向主服务器发送
PSYNC命令,请求同步数据 - 主服务器收到命令后,执行
BGSAVE命令,在后台生成 RDB 文件 - 主服务器将 RDB 文件发送给从服务器,同时将新的写命令缓存在内存中
- 从服务器接收到 RDB 文件后,清空自己的数据库,然后加载 RDB 文件到内存中
- 主服务器将缓存的写命令发送给从服务器,从服务器执行这些命令,保证数据一致性
注意:在全量同步阶段,从服务器会等待主服务器发送 RDB 文件和缓存的写命令,这个过程是同步的,从服务器在加载 RDB 文件和执行缓存命令时,无法处理客户端的请求。
触发全量同步的场景:
- 从服务器首次连接主服务器:必须进行全量同步以获取完整的数据副本
-
主从复制中断时间过长:当断开时间超过
repl-backlog-size缓冲区容量时,触发全量同步 - 主服务器重启:主服务器重启后自身 runid(复制ID) 变更,从库重连时携带旧的主库复制ID,主库无法匹配识别,触发全量同步
注意:从服务器重启仅自身 runid 变化,不影响复制协商,只要主库复制ID未变、偏移量仍在复制积压缓冲区范围内,依旧可以走增量同步,不会触发全量同步
增量同步(异步)
在全量同步完成后,主从关系建立,从节点不再发送 PSYNC 命令。增量同步阶段,主节点主动持续推送写命令给从节点,从节点不主动请求数据。
复制积压缓冲区特点:
- 主库统一管理:主库维护一个复制积压缓冲区,所有从库共享该缓冲区
-
大小可调整:通过
repl-backlog-size参数配置,默认 1MB,建议根据主库写命令频率和从库处理能力调整 - 环形缓冲区:采用环形设计,缓冲区满后新数据覆盖最旧数据
- 增量同步条件:只有当从节点的偏移量仍在缓冲区范围内,才能增量同步;若偏移量被覆盖,主库直接推送缓冲区中的后续命令,从库会跳过丢失的命令,导致数据丢失但最终与主库保持当前数据一致
重要提醒:正常连接时也可能发生丢数据,不只是断开重连才丢。当从库处理速度较慢(如网络延迟、从库负载高等),即使连接正常,从库上报的 offset 也可能被新命令覆盖,导致丢段但不触发全量,主从最终一致但历史操作丢失,只有下次全量复制时才会补充。因此平时要合理设置 repl-backlog-size,避免正常复制过程中因缓冲区过小导致数据丢失。
触发增量同步的场景:
- 全量同步结束后自动进入,无需额外协商
- 主从复制正常运行期间,主服务器持续将写命令同步给从服务器
- 主从连接短暂中断后恢复,且中断期间的数据变更仍在 repl-backlog 缓冲区中
心跳检测
全量同步完成后,从节点通过心跳机制与主节点保持连接:
心跳消息:
- 从节点发送 PING(默认每秒一次),携带自身当前的复制偏移量
- 主节点响应 PONG,仅表示连接正常,不携带偏移量信息
重要结论:
- 偏移量单向传递:只有从 → 主,主节点不会反向发送自己的偏移量给从节点
- 心跳是同步的依据:主库在正常运行时会主动推送新命令,但推送的前提和依据,完全来自从库每秒心跳上报的偏移量;没有心跳,主库既不知道推到哪,也不知道从库缺了啥
- 主节点判断并推送:主节点根据从节点汇报的偏移量,自行判断从节点缺了哪些命令,然后主动推送过去,从节点不请求数据
增量同步流程:
主节点执行写命令
↓
命令追加到 repl-backlog 缓冲区
↓
主节点根据从节点汇报的 偏移量 判断缺失数据
↓
主节点 主动推送 缺失的命令给从节点
↓
从节点接收并执行命令,更新偏移量
注意:PSYNC 命令只在建立复制关系或重连时发送一次,由主节点根据 replication ID 和 offset 决定是全量同步还是增量同步。全量同步结束后自动进入增量同步阶段,无需额外协商。
1.3 配置示例
主节点配置
# 监听端口
port 6379
# 设置密码认证
requirepass your_master_password
# 连接主节点时的密码(用于主从复制)
masterauth your_master_password
# 允许所有IP连接(生产环境建议指定具体IP)
bind 0.0.0.0
# 后台运行
daemonize yes
从节点配置
# 监听端口
port 6380
# 指定主节点地址和端口(主从复制配置,Redis 5.0+ 推荐使用 replicaof)
replicaof 127.0.0.1 6379
# 连接主节点时使用的密码
masterauth your_master_password
# 从节点密码
requirepass your_slave_password
# 允许所有IP连接
bind 0.0.0.0
# 后台运行
daemonize yes
1.4 Spring Boot 配置
配置方式:手动写死主节点和从节点地址
读写控制:客户端代码自己控制写主、读从
spring:
redis:
password: your_master_password
database: 0
master:
host: 127.0.0.1
port: 6379
slaves:
hosts: 127.0.0.1:6380,127.0.0.1:6381 # 多从节点
@Configuration
public class RedisConfig {
@Value("${spring.redis.master.host}")
private String masterHost;
@Value("${spring.redis.master.port}")
private int masterPort;
@Value("${spring.redis.slaves.hosts}")
private String slaves;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private int database;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
// 主节点
RedisStandaloneConfiguration master = new RedisStandaloneConfiguration(masterHost, masterPort);
master.setPassword(password);
master.setDatabase(database);
// 从节点列表
List<RedisStandaloneConfiguration> slavesList = Arrays.stream(slaves.split(","))
.map(s -> {
String[] hp = s.split(":");
RedisStandaloneConfiguration c = new RedisStandaloneConfiguration(hp[0], Integer.parseInt(hp[1]));
c.setPassword(password);
c.setDatabase(database);
return c;
}).toList();
// 关键:创建主从拓扑
MasterSlaveConnectionFactory factory = new MasterSlaveConnectionFactory(master, slavesList);
factory.setReadFrom(ReadFrom.REPLICA_PREFERRED); // 现在才真正生效!
return factory;
}
}
1.5 主从模式优缺点
| 优点 | 缺点 |
|---|---|
| 配置简单,易于上手 | 主节点是单点,故障后需要人工干预(重启主节点/将从节点升级为主节点等等) |
| 实现读写分离,提高读取吞吐量 | 主从同步时数据可能短暂不一致 |
| 数据冗余,提高数据安全性 | 无法自动实现故障转移 |
| 适合数据量小的场景 | 垂直扩展受单机硬件限制 |
2. Redis 哨兵模式(Sentinel Mode)
2.1 原理介绍
哨兵模式是基于主从模式的高可用方案,通过部署多个哨兵(Sentinel)进程来监控主从节点的运行状态,并在主节点故障时自动完成故障转移。
核心功能:
- 监控(Monitoring):Sentinel 持续检查主服务器和从服务器是否正常运行
- 通知(Notification):当被监控的 Redis 实例出现问题时,Sentinel 通过 API 向管理员发送通知
- 自动故障转移(Automatic Failover):当主服务器不能正常工作时,Sentinel 自动将一个从服务器升级为新的主服务器,并让其他从服务器开始复制新的主服务器
- 配置提供者(Configuration Provider):客户端连接 Sentinel 获取当前 Redis 主节点的地址
哨兵模式本质上是一个运行在特殊模式下的 Redis 进程,用于监控主从结构中的各个实例。通常建议至少部署 3 个哨兵实例以确保高可用性。
2.2 哨兵模式工作原理
- 主节点监控:哨兵节点通过配置文件指定要监控的主节点,自动发现关联的从节点
-
哨兵互相发现:哨兵彼此不靠配置,靠主库上的 Pub/Sub 频道(
__sentinel__:hello)互相发现,通过定期发布消息来感知其他哨兵的存在 - 多哨兵协作:多个哨兵节点协同工作,通过投票机制判断主节点是否失效
- 故障检测:当主节点在指定时间内没有回复 PING,则认为它主观下线,达到 quorum 配置值的多个哨兵确认后认定为客观下线
- 故障转移:从剩余的从节点中选举一个新的主节点,并更新其他从节点的复制目标
2.3 故障转移详解
主观下线(SDown)
↓
多个哨兵确认(达到 quorum)
↓
客观下线(ODown)
↓
哨兵投票选举leader
↓
选举从节点成为新主节点
↓
更新从节点复制目标
↓
旧主节点降级为从节点
故障转移过程:
- 当主节点被认定为客观下线后,所有监视该主节点的哨兵进行投票
- 被选为 leader 的哨兵负责执行故障转移
- 从所有从节点中选举一个成为新的主节点(选举规则:优先级 > 偏移量 > runid)
- leader 哨兵让其他从节点去复制新的主节点
- 哨兵自动将恢复的原主节点降级为从节点,使其追随新选举的主节点
2.4 配置示例
主节点配置
port 6379
requirepass your_master_password
masterauth your_master_password
bind 0.0.0.0
daemonize yes
从节点配置
port 6380
slaveof 127.0.0.1 6379
masterauth your_master_password
requirepass your_slave_password
bind 0.0.0.0
daemonize yes
哨兵配置
# 哨兵监听端口
port 26379
# 监控的主节点名称、IP、端口和法定人数
# 格式:sentinel monitor <mastername> <ip> <port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# 主节点密码认证
sentinel auth-pass mymaster your_master_password
# 判断主节点主观下线的毫秒数
sentinel down-after-milliseconds mymaster 5000
# 故障转移时,同时进行复制的从节点数量
sentinel parallel-syncs mymaster 1
# 故障转移超时时间(毫秒)
sentinel failover-timeout mymaster 10000
# 允许所有IP连接
bind 0.0.0.0
# 后台运行
daemonize yes
2.5 Spring Boot 配置
配置方式:只写2~3个哨兵种子节点
读写控制:客户端问哨兵拿主节点写主;自动发现所有从节点读从
spring:
redis:
# 哨兵模式配置
sentinel:
# 主节点名称
master: mymaster
# 哨兵节点列表
nodes:
- 127.0.0.1:26379
- 127.0.0.1:26380
- 127.0.0.1:26381
# Redis密码
password: your_master_password
# 连接超时时间
timeout: 2000ms
# 数据库索引(默认0)
database: 0
# Lettuce配置(读写分离)
lettuce:
client-options:
read-from: REPLICA_PREFERRED # 优先从从节点读
2.6 哨兵模式优缺点
| 优点 | 缺点 |
|---|---|
| 自动故障转移,无需人工干预 | 不支持数据分片,所有数据存储在主节点 |
| 客户端无需知道主节点地址,通过哨兵获取 | 故障转移期间可能出现短时不可用 |
| 每个哨兵节点都可以提供故障转移能力 | 仍然基于主从复制,数据同步存在延迟 |
| 适合数据量不大但需要高可用的场景 | 最小需要 5 个节点(1主1从+3哨兵) |
3. Redis 集群模式(Cluster Mode)
3.1 原理介绍
集群模式是 Redis 的分布式解决方案,通过数据分片和高可用机制实现水平扩展和故障转移。
核心特点:
- 数据分片:数据自动分片到多个节点上,每个节点负责一部分哈希槽(hash slot)
- 高可用性:每个主节点可以有多个从节点,实现主从复制和自动故障转移
- 去中心化:集群中的每个节点都保存着整个集群的状态信息,没有中心节点
- 客户端路由:客户端可以连接任意节点,节点会将请求重定向到正确的节点
3.2 为什么集群模式不需要哨兵
Redis Cluster 与哨兵模式相比,具有以下关键差异,因此不需要额外的哨兵组件:
-
内置高可用性:
- Redis Cluster 本身已经内置了故障检测和自动故障转移机制
- 每个节点都参与集群状态的维护和故障检测
- 不需要外部组件来监控节点状态
-
去中心化架构:
- 集群中的每个节点都保存完整的集群状态信息
- 节点之间通过 Gossip 协议交换状态信息
- 没有单点故障问题
-
自动主从管理:
- 集群在创建时就配置了主从关系
- 当主节点失效时,相关从节点会自动进行故障转移
- 整个过程由集群内部协议处理,无需外部干预
-
分布式决策:
- 故障检测和主从切换由集群内多数节点共同决定
- 避免了哨兵模式中可能出现的网络分区问题
故障转移机制详解
Redis Cluster 的故障转移流程如下:
主观下线(PFAIL)
↓
超过半数主节点确认
↓
客观下线(FAIL)
↓
从节点发起选举
↓
主节点投票
↓
获得超过半数票的从节点成为新主节点
| 阶段 | 发起者 | 参与者 | 说明 |
|---|---|---|---|
| 主观下线(PFAIL) | 任何节点(主/从) | - | 节点通过 PING 检测到某节点超时未响应,标记为 PFAIL(Possible Fail) |
| 客观下线(FAIL) | 发现 PFAIL 的节点 | 主节点投票 | 当超过半数主节点认为某主节点下线时,将其标记为 FAIL |
| 选举发起 | 从节点 | - | 从节点发现主节点被标记为 FAIL 后,发起选举请求 |
| 投票选举 | - | 主节点投票 | 主节点投票,获得超过半数票的从节点成为新的主节点 |
关键点:
- 所有节点都参与集群状态维护和故障检测(通过 Gossip 协议交换状态)
- 主观下线:任何节点都可以标记
- 客观下线:只有主节点有投票权
- 选举发起:从节点发起选举请求
- 投票:只有主节点参与投票
为什么主节点数量必须 ≥ 3?
"超过半数"的硬性要求是基于 Raft 协议 的分布式一致性原则,不可配置。这直接决定了主节点数量必须 ≥ 3。
假设只有 2 个主节点:
| 主节点 | 状态 | 投票权 |
|---|---|---|
| M1 | ❌ 宕机 | 无法投票 |
| M2 | ✅ 正常 | 1 票 |
- 总票数:2 票
- M2 的票数:1 票(50%)
- 需要超过半数:> 1 票,即至少 2 票
- 结果:选举失败,无法选出新主节点 ❌
集群是否可用? 取决于 cluster-require-full-coverage 配置:
| 配置值 | 结果 |
|---|---|
yes(默认) |
集群完全不可用,任何请求都返回错误 |
no |
部分可用,M1 负责的槽位不可用,M2 负责的槽位仍可正常服务 |
不同主节点数量的容错能力:
| 主节点数 | 挂掉 1 个后 | 剩余票数 | 需要票数(> N/2) | 能否选举 | 最大容错节点数 |
|---|---|---|---|---|---|
| 2 | 1 个 | 1 票 | > 1(需 2 票) | ❌ 不能 | 0 |
| 3 | 2 个 | 2 票 | > 1.5(需 2 票) | ✅ 能 | 1 |
| 5 | 4 个 | 4 票 | > 2.5(需 3 票) | ✅ 能 | 2 |
| 7 | 6 个 | 6 票 | > 3.5(需 4 票) | ✅ 能 | 3 |
结论:
- 主节点数量必须是奇数(3、5、7...),因为偶数并不能提高容错能力
- 3 个主节点可容忍 1 个节点故障
- 5 个主节点可容忍 2 个节点故障
- 生产环境推荐 3 个主节点,足以满足大多数场景的高可用需求
3.3 自动分配主从节点机制
Redis Cluster 在创建时会自动分配主从节点关系:
-
节点角色分配:
- 在创建集群时,通过
--cluster-replicas参数指定每个主节点的从节点数量 - 系统自动将节点分为主节点和从节点
- 在创建集群时,通过
-
槽位分配:
- 16384 个哈希槽被均匀分配给主节点
- 每个从节点知道自己对应的主节点
-
故障转移机制:
- 当主节点失效时,其从节点会通过选举产生新的主节点
- 其他从节点会更新复制目标到新的主节点
3.4 数据分片机制
Redis Cluster 采用哈希槽机制实现数据分片:
- 哈希槽划分:整个集群被划分为 16384 个哈希槽(0-16383)
- 键到槽映射:每个 key 通过 CRC16 算法计算哈希值,然后对 16384 取模得到槽位号
slot = CRC16(key) % 16384
- 槽位分配:这些槽位会均匀分布在不同的主节点上
槽位分配示例:
| 主节点数 | 槽位分配 |
|---|---|
| 3主 | 0-5460 (5461槽), 5461-10921 (5461槽), 10922-16383 (5462槽) |
| 4主 | 0-4095 (4096槽), 4096-8191 (4096槽), 8192-12287 (4096槽), 12288-16383 (4096槽) |
| 6主 | 0-2730 (2731槽), 2731-5461 (2731槽), 5462-8192 (2731槽), 8193-10922 (2730槽), 10923-13653 (2731槽), 13654-16383 (2730槽) |
注意:16384 无法被 3 和 6 整除,因此槽位分配会有 1-2 个槽的差异。
Hash Tag 机制
当 key 包含 { 和 } 时,只有花括号内的部分会被用于哈希计算:
-
user:1000:profile和user:1000:settings→ 不同槽位 -
{user:1000}:profile和{user:1000}:settings→ 相同槽位
应用场景: 保证相关数据存储在同一节点上,支持多键操作(如 MGET {user:1000}:profile {user:1000}:settings)
3.5 集群模式工作原理
-
节点发现:通过
MEET消息发现新节点 -
槽位分配:通过
CLUSTER ADDSLOTS命令分配槽位 - 状态同步:节点间通过 Gossip 协议交换集群状态(IP、端口、运行ID、槽位信息)
-
请求路由:
- 节点存储映射:每个节点都存储完整的哈希槽与节点的对应关系
- 客户端缓存映射:客户端也会缓存一份哈希槽映射关系,负责将请求直接发送到正确的节点
- MOVED 重定向:当集群扩容/缩容导致客户端缓存的旧映射失效,请求发送到错误的节点时,节点会返回 MOVED 重定向,客户端据此更新映射关系并重新发送请求到正确的节点
- 故障检测:通过 PING/PONG 消息检测节点状态
- 故障转移:从节点检测到主节点失效后发起选举,成为新的主节点
3.6 客户端类型
Redis 集群客户端分为智能客户端和非智能客户端两种类型:
| 类型 | 特点 | 性能 |
|---|---|---|
| 非智能客户端 | 不缓存槽位映射,每次请求可能需要重定向 | 较低,多次网络往返 |
| 智能客户端 | 缓存槽位映射,直接将请求发送到正确节点 | 较高,减少重定向次数 |
智能客户端工作流程
- 初始化:启动时从集群节点获取完整的槽位映射关系
- 本地缓存:缓存哈希槽与节点的对应关系
- 直接路由:根据缓存的映射,直接将请求发送到正确的节点
-
自动更新:收到
MOVED或ASK响应时,自动更新本地缓存
Spring Boot 客户端
Spring Boot 默认使用 Lettuce 作为 Redis 客户端(也可选择 Jedis),这两种客户端在集群模式下默认都是智能客户端:
- Lettuce:基于 Netty 的异步客户端,支持连接池,默认智能路由
- Jedis:传统同步客户端,支持连接池,默认智能路由
spring:
redis:
cluster:
nodes: 127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002
max-redirects: 3
# Lettuce 连接池配置(可选)
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
3.7 集群创建命令
# 创建集群(3主3从,每个主节点1个从节点)
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
3.8 配置示例
节点配置
# 节点监听端口
port 7000
# 开启集群模式
cluster-enabled yes
# 集群配置文件(自动生成和维护)
cluster-config-file nodes-7000.conf
# 节点间通信超时时间(毫秒)
cluster-node-timeout 15000
# 是否要求所有槽位都被覆盖才提供服务
cluster-require-full-coverage no
# 设置密码认证
requirepass your_cluster_password
# 连接主节点时的密码(用于主从复制)
masterauth your_cluster_password
# 允许所有IP连接
bind 0.0.0.0
# 后台运行
daemonize yes
3.9 Spring Boot 配置
配置方式:只写2~3个集群种子节点
读写控制:客户端自动拉取全集群拓扑,写主节点、读从节点自动路由
spring:
redis:
# 集群模式配置
cluster:
# 集群节点地址列表(只需写2~3个种子节点)
nodes:
- 127.0.0.1:7000
- 127.0.0.1:7001
- 127.0.0.1:7002
# 最大重定向次数
max-redirects: 3
# Redis密码
password: your_cluster_password
# 连接超时时间
timeout: 2000ms
# Lettuce配置(读写分离)
lettuce:
client-options:
read-from: REPLICA_PREFERRED # 优先从从节点读
3.10 集群模式优缺点
| 优点 | 缺点 |
|---|---|
| 支持数据分片,实现水平扩展 | 多键操作受限,仅支持同一槽位 |
| 内置高可用,无需额外组件 | 客户端需要支持集群协议 |
| 每个节点都参与集群管理,无单点故障 | 事务支持仅限同一节点 |
| 适合数据量大,需要分片+高可用的场景 | 最小需要 6 个节点(3主3从) |
多键操作限制详解
集群模式下,不同类型的操作对跨槽的处理方式不同:
| 操作类型 | 跨槽支持 | 说明 |
|---|---|---|
| 普通批量命令(MGET/MSET/DEL) | ❌ 必须同槽 | 跨槽直接报错:CROSSSLOT Keys in request don't hash to the same slot
|
| Hash Tag | ✅ 强制同槽 | 使用 {key} 确保相关 key 在同一槽位,性能最好 |
| Pipeline | ⚠️ 可跨槽 | 客户端自动处理重定向,性能较差,无原子性保证 |
| Lua 脚本 / 事务 | ❌ 必须同槽 | 与普通批量命令相同,必须在同一个节点上执行 |
Hash Tag 示例:
# ❌ 报错:user:100 和 user:200 可能不在同一槽位
MGET user:100 user:200
# ✅ 正常:{user} 决定槽位,profile 和 settings 都在同一槽位
MGET {user}:profile {user}:settings
选择建议:
- 性能优先:使用 Hash Tag 确保同槽
- 开发便利性:可以使用 Pipeline,但需注意性能损耗
- 强一致性:Lua 脚本和事务必须同槽
3.11 数据倾斜问题
现象
热点槽、热点大 Key、热点读/写集中在单节点,导致节点 CPU / 内存 / 网卡打满,集群负载失衡。
成因
| 成因 | 说明 |
|---|---|
| 固定 HashTag | 过度使用同一个 HashTag,导致大量 key 集中在一个槽位 |
| 业务分区键单一 | 业务设计时只用一个字段做 hash,无法分散到不同节点 |
| 冷热数据未分离 | 热点数据和冷数据混在一起,热点数据占用大量资源 |
| 大 Key 集中 | 单个 key 的 value 特别大,导致该节点负载过高 |
优化方案
1. 打散热点 Key
-
增加随机后缀:将热点 key 拆分为多个副本,分散到不同槽位
hot:user:100 → hot:user:100:0, hot:user:100:1, hot:user:100:2 -
结合 HashTag:使用 HashTag 确保同槽操作
{user:100}:profile:{0}, {user:100}:profile:{1}, {user:100}:profile:{2}
2. 拆分大 Key
| 类型 | 优化方法 |
|---|---|
| 大 Hash | 拆分为多个小 Hash,使用 HashTag 确保同槽 |
| 大 List | 拆分为多个 List,按范围或 hash 分桶 |
| 大 Set | 按数据类型或时间维度分桶 |
3. 冷热分离
- 热点数据放 Redis:频繁访问的数据保持在 Redis
- 冷数据落库:不常访问的数据定期持久化到数据库
- 本地缓存兜底:读多写少场景,使用本地缓存(如 Caffeine、Guava Cache)减轻 Redis 压力
4. 监控告警
| 监控项 | 告警阈值建议 |
|---|---|
| 槽位负载 | 单槽 key 数量超过平均值 200% |
| Key 大小 | 单个 key 超过 10MB |
| 节点 CPU | 节点 CPU 使用率超过 80% |
| 节点内存 | 节点内存使用率超过 80% |
监控命令:
# 查看各节点 key 数量
redis-cli --cluster info 127.0.0.1:7000
# 查看槽位分布
redis-cli --cluster check 127.0.0.1:7000
# 扫描大 key(Redis 4.0+)
redis-cli --bigkeys -i 0.1
4. 三种模式对比
| 特性 | 主从模式 | 哨兵模式 | 集群模式 |
|---|---|---|---|
| 数据分片 | 不支持 | 不支持 | 支持(16384个哈希槽) |
| 扩展性 | 垂直扩展 | 垂直扩展 | 水平扩展 |
| 高可用 | 无(需要人工干预) | 自动故障转移 | 自动故障转移 |
| 主从管理 | 手动配置 | 哨兵自动管理 | 集群自动管理 |
| 客户端复杂度 | 简单 | 简单 | 需要支持集群协议 |
| 多键操作 | 完全支持 | 完全支持 | 仅支持同一槽位 |
| 事务支持 | 完全支持 | 完全支持 | 仅限同一节点 |
| 最小节点数 | 2个(1主1从) | 5个(1主1从+3哨兵) | 6个(3主3从) |
| 适用场景 | 数据量小,基础读写分离 | 数据量小,需要高可用 | 数据量大,需要分片+高可用 |
核心区别总结
| 模式 | 配置方式 | 读写控制 | 核心特点 |
|---|---|---|---|
| 主从模式 | 手动写死主从节点地址 | 客户端代码自己控制写主、读从 | Redis 只负责数据同步,不负责路由和故障转移 |
| 哨兵模式 | 只写2~3个哨兵种子节点 | 客户端问哨兵拿主节点写主;自动发现所有从节点读从 | 哨兵负责故障转移、主从切换 |
| 集群模式 | 只写2~3个集群种子节点 | 客户端自动拉取全集群拓扑,写主节点、读从节点自动路由 | 分片 + 高可用,自带故障转移 |
一句话口诀:
主从自己写,哨兵找主从,集群全自动。
5. 总结
Redis 的三种高可用模式适用于不同的场景:
主从模式:适用于数据量不大,只需要读写分离和数据备份的场景。配置简单,但主节点故障需要人工干预。
哨兵模式:在主从模式基础上增加了自动故障转移功能,适用于数据量不大但需要高可用的场景。缺点是不支持数据分片。
集群模式:通过数据分片实现水平扩展,同时具备高可用机制,适用于数据量大、需要分片和分片内高可用的现代分布式应用。
重要结论:
- 哨兵模式和集群模式在主从复制的基本机制上是相似的,都基于 PSYNC2 协议,都支持全量和增量同步
- 集群模式内置了高可用机制,不需要额外的哨兵组件
- 选择哪种模式主要取决于数据量大小、性能要求和系统复杂度等因素
推荐:
- 小规模应用(数据量 < 10GB):使用哨兵模式
- 大规模应用(数据量 > 10GB):使用集群模式
- 集群模式因为其内置的高可用性和自动主从管理机制,更适合现代分布式应用的需求。