Redis Cluster 是 Redis 的分布式解决方案,但"能用"和"好用"之间隔着一百个坑。这篇文章基于我们团队维护的 200+ 节点 Redis Cluster 集群的实战经验,讲清楚从搭建到优化的完整路径。
一、Cluster 架构的核心概念
Redis Cluster 采用无中心架构,16384 个哈希槽(slot)均匀分布在各节点上。每个 key 通过 CRC16(key) % 16384 计算归属槽,再路由到对应节点。
plain
css
节点 A:槽 0-5460
节点 B:槽 5461-10922
节点 C:槽 10923-16383
无中心意味着没有单点瓶颈,但也意味着客户端必须"集群感知"------能缓存槽映射、能处理 MOVED/ASK 重定向。
二、搭建:别用 redis-trib.rb 了
老教程让你用 redis-trib.rb 创建集群,但 Redis 5.0+ 已经内置 redis-cli --cluster:
bash
lua
# 创建 3 主 3 从的集群
redis-cli --cluster create \
192.168.1.10:6379 \
192.168.1.11:6379 \
192.168.1.12:6379 \
192.168.1.13:6379 \
192.168.1.14:6379 \
192.168.1.15:6379 \
--cluster-replicas 1
--cluster-replicas 1 表示每个主节点配一个从节点。Redis 会自动分配槽和主从关系。
但生产环境别这么干。上面的命令把主从节点放在同一台物理机上,物理机挂了,主从一起死,高可用形同虚设。
正确的拓扑:跨机架部署
plain
css
机架 1:主 A,从 B
机架 2:主 B,从 C
机架 3:主 C,从 A
每个主节点的从节点放在不同机架,机架级故障不会导致数据丢失。
三、客户端配置:集群感知是关键
Jedis 和 Lettuce 都支持 Cluster 模式,但默认配置在生产环境会出问题。
Jedis Cluster 的正确打开方式:
java
javascript
Set<HostAndPort> nodes = Set.of(
new HostAndPort("192.168.1.10", 6379),
new HostAndPort("192.168.1.11", 6379),
new HostAndPort("192.168.1.12", 6379)
);
JedisCluster jedis = new JedisCluster(nodes,
2000, // connectionTimeout
2000, // soTimeout
3, // maxAttempts
"password",
new JedisPoolConfig() {{
setMaxTotal(100);
setMaxIdle(50);
setMinIdle(10);
}}
);
关键参数:
maxAttempts=3:遇到 MOVED/ASK 重试 3 次,避免瞬时故障导致请求失败connectionTimeout=2000:连接超时 2 秒,太长会阻塞业务线程soTimeout=2000:读取超时 2 秒,防止慢查询拖垮客户端
Lettuce 的集群模式更智能:
java
less
RedisClusterClient client = RedisClusterClient.create(
RedisURI.builder()
.withHost("192.168.1.10")
.withPort(6379)
.withPassword("password")
.build()
);
StatefulRedisClusterConnection<String, String> connection = client.connect();
connection.setReadFrom(ReadFrom.REPLICA_PREFERRED); // 优先读从节点
ReadFrom.REPLICA_PREFERRED 让读请求优先路由到从节点,分担主节点压力。但注意:从节点的数据有延迟,对一致性要求高的读操作(比如读后立即写)必须走主节点。
四、热点 Key 的识别与拆分
Redis Cluster 最大的性能杀手不是节点宕机,是热点 Key。
假设你有一个 product:10086:stock 的库存 key,秒杀时每秒 10 万次访问,这个 key 所在的槽和节点会被打爆,其他槽空闲,集群利用率严重不均。
识别热点 Key:
bash
perl
# Redis 4.0+ 支持 hotkeys 参数
redis-cli --hotkeys
# 或者通过 INFO 命令查看
redis-cli INFO stats | grep keyspace
拆分策略:虚拟 key + 本地缓存
java
ini
// 把热点 key 拆成 10 个虚拟 key
String virtualKey = "product:10086:stock:" + (userId % 10);
int stock = jedis.get(virtualKey);
// 应用层做本地缓存(Caffeine)
LoadingCache<String, Integer> localCache = Caffeine.newBuilder()
.expireAfterWrite(100, TimeUnit.MILLISECONDS)
.build(key -> jedis.get(key));
虚拟 key 把流量分散到 10 个槽,本地缓存进一步减少对 Redis 的访问。但本地缓存的过期时间必须短(100ms 以内),否则数据不一致。
五、大 Key 的治理
另一个隐形杀手是大 Key。一个 Hash 里有 100 万个 field,或者一个 String 存了 10MB 的 JSON,都会导致:
- 序列化/反序列化耗时
- 网络传输阻塞
- 主从复制时 RDB 生成卡顿
扫描大 Key:
bash
css
redis-cli --bigkeys
或者自定义脚本扫描:
bash
bash
redis-cli --eval scan_bigkeys.lua
治理方案:拆分 + 压缩
java
sql
// 大 Hash 拆成多个小 Hash
// 原:HSET user:10086 profile "{...10MB JSON...}"
// 拆:
HSET user:10086:profile:basic name "张三" age 25
HSET user:10086:profile:extra tags "vip,premium"
String 类型的大 value 用 Snappy 或 LZ4 压缩:
java
ini
byte[] compressed = Snappy.compress(jsonString.getBytes());
jedis.set("user:10086:profile", compressed);
读取时先解压再反序列化。压缩率通常在 60%-80%,网络传输和内存占用大幅降低。
六、持久化与容灾的权衡
Redis Cluster 的持久化有两种:RDB 和 AOF。
RDB: 定时快照,文件紧凑,恢复速度快,但会丢数据(两次快照之间的数据丢失)。
AOF: 记录每条写命令,数据安全性高,但文件大、恢复慢。
生产环境的推荐配置:
conf
bash
# 同时开启 RDB 和 AOF
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes # Redis 4.0+ 混合持久化
aof-use-rdb-preamble yes 开启混合持久化:AOF 文件前半部分是 RDB 快照,后半部分是增量 AOF 命令。恢复时先加载 RDB 快速恢复大部分数据,再重放 AOF 补全,兼顾速度和安全性。
跨机房容灾:
主从复制只能防单节点故障,防不了机房级灾难。我们的做法是多活架构:
plain
css
北京机房:主 A,主 B,主 C
上海机房:从 A,从 B,从 C
写操作走北京,读操作可以走上海。两个机房之间通过专线同步,延迟控制在 5ms 以内。
但跨机房复制有带宽成本,且网络抖动时从节点会被误判为故障。需要调整:
conf
bash
cluster-node-timeout 30000 # 默认 15 秒,跨机房调到 30 秒
七、监控与告警
没有监控的 Redis 集群是定时炸弹。我们监控的核心指标:
表格
| 指标 | 阈值 | 说明 |
|---|---|---|
used_memory / maxmemory |
> 85% | 内存告警,考虑扩容或淘汰策略 |
instantaneous_ops_per_sec |
> 100000 | 单节点 QPS 过高,检查热点 |
rejected_connections |
> 0 | 连接数超限,检查客户端池配置 |
keyspace_hits / keyspace_misses |
命中率 < 80% | 缓存失效严重,检查 key 设计和过期策略 |
slowlog |
命令耗时 > 10ms | 慢查询告警,优化命令或拆分大 key |
Prometheus + Grafana 是标配,配合 Redis Exporter 采集指标。
八、总结
Redis Cluster 不是搭起来就能跑的生产系统。从拓扑设计、客户端配置、热点治理、大 key 拆分、持久化策略到跨机房容灾,每个环节都有坑。
几个核心原则:
- 主从跨机架部署,别放同一台物理机
- 客户端必须集群感知,配置重试和超时
- 热点 key 拆虚拟 key + 本地缓存
- 大 key 必须拆分或压缩
- 混合持久化(RDB + AOF)是生产标配
- 监控比调优更重要,先监控再优化
Redis 很快,但快的前提是"用对"。希望这些实战经验能帮你少踩几个坑。