因为hotkey始终映射到同一哈希槽和节点,单节点承受全部请求,在单线程模型下导致CPU、网络、内存资源耗尽,引发延迟飙升甚至进程被OOM killer终止。

为什么直接用 GET hotkey 会压垮一个节点
Redis Cluster 默认用 CRC16(key) % 16384 算出 slot,再映射到具体实例。如果业务中反复读写同一个 key(比如 stock:1001),它永远落在同一个 slot、同一个实例上。哪怕集群有 6 个节点,99% 的请求也只打向其中 1 个——这不是负载均衡,是“负载集中”。单线程模型下,CPU、网络连接、内存带宽全被吃满,响应延迟飙升,甚至触发 OOM killer 杀进程。
hash tag 不是万能解,反而可能加剧倾斜
很多人看到文档里说“用 {user123} 包裹 key 就能强制同 slot”,就盲目套用。但问题在于:{user123}:order:20261019 和 {user123}:profile 是同 slot,可如果 user123 是超级用户,所有关联 key 都挤进同一实例,照样形成新热点。更糟的是,hash tag 本质是放弃分片自由度,把多个逻辑 key 锁死在物理节点上,等于主动制造数据量倾斜。
- 仅在「强一致性要求 + 访问必然聚合」场景下才考虑,比如分布式锁的多个子 key 必须原子操作
- 绝对不要对高频读写的业务 key(如商品库存、用户计数)加
{} - 用
CLUSTER KEYSLOT "key"手动验证 tag 后的 slot 分布,避免误判
真正有效的 key 打散:按访问维度做随机分片
核心思路是把一个逻辑 key 拆成 N 个物理 key,让它们自然落到不同 slot 上。关键不是“均匀”,而是“不可预测性”——防止客户端缓存或重试逻辑无意中又把流量导回同一份 shard。
- 写入时用
user_id或request_id做哈希后取模:f"stock:{item_id}:shard{hashlib.md5(user_id.encode()).hexdigest()[-2:]}" - 读取时必须走同样哈希逻辑,不能靠客户端缓存原始 key,否则打散失效
- 过期时间要加随机偏移(比如 ±3 秒),避免所有 shard 同时失效引发雪崩
- 注意:打散后无法原子操作全部 shard,需在应用层做聚合(如累加 N 个
INCRBY结果)
Slot 分配不均必须人工干预,别信“自动均衡”
Redis Cluster 不会自动 rebalance slot,运维手动分配时若给某节点多划了 3000 个 slot,它就会比其他节点多存近一倍数据。即使没热点 key,纯数据量倾斜也会导致内存告警、RDB 备份慢、AOF rewrite 卡顿。
- 用
CLUSTER SLOTS查各节点 slot 范围,再用CLUSTER COUNTKEYSINSLOT抽样统计 key 数量 - 迁移前先在目标节点执行
CLUSTER SETSLOTIMPORTING - 源节点执行
CLUSTER SETSLOTMIGRATING - 用
CLUSTER GETKEYSINSLOT分批获取 key,再用MIGRATE迁移(加KEYS参数可一次传多个)
打散 key 和调平 slot 是两件事:前者治“热”,后者治“重”。漏掉任何一项,都可能在大促峰值时发现某个节点 CPU 100% 却查不出原因——因为监控只显示“QPS 正常”,没人盯着单节点内存和 slot 分布。











