先查槽位是否真不均,再确认热点Key或Hash Tag作祟,最后检查客户端是否卡在过载节点上拉取错误槽表;用cluster nodes看槽分布、countkeysinslot查键量、--bigkeys扫大Key,避免连续ID等高集中度字段滥用Hash Tag。
Redis集群节点负载不均衡,通常不是“算法坏了”,而是槽位分配、键设计或客户端行为没对齐真实流量分布。直接看结论:先查槽位是否真不均,再确认是不是热点Key或Hash Tag在作祟,最后检查客户端是否卡在过载节点上拉取了错误的槽表。
用 redis-cli -c -h {host} -p {port} cluster nodes 查每个节点负责的槽数,注意看输出里每行末尾的 slot 范围(如 0-5460)。16384 个槽平均分给 N 个 master,理想偏差应小于 ±1%。如果某节点占了 8000+ 槽,而另一节点只有 2000,那就是硬性不均。
别信 redis-cli --cluster rebalance 显示的 “no rebalancing needed”——它只按槽数算,不看 key 数量、访问频次或命令复杂度。实际中,一个槽里存 1 个大 Hash 和存 1000 个 String,压力天差地别。
cluster countkeysinslot {slot} 抽样查几个高槽号和低槽号,对比 key 数量级redis-cli --bigkeys 扫描全集群,确认有没有单节点集中了多个 bigkeyHash Tag({...})本意是让相关 key 落在同一槽,但业务上若滥用,比如所有用户会话都用 session:{uid},而 uid 是连续自增 ID(如 100001、100002…),CRC16 对连续数字的哈希结果容易聚集在相邻槽,最终还是压到同一节点。
更隐蔽的问题是:哪怕你用了 user:{123}:profile 和 user:{123}:orders,它们确实落在同槽,但如果 {123} 是超级用户,这个槽就成了事实上的热点槽——槽均衡 ≠ 请求均衡。
user:{123#rand8}:profile,用随机后缀打散node-redis、redis-py 等主流客户端首次连接时,会从配置列表里随机挑一个节点发 CLUSTER SLOTS,拿到槽表后就缓存起来。如果这个“幸运节点”本身槽位过载(比如被手动分配了 10000 槽),客户端后续所有路由都基于它返回的表计算——它不会因为自己慢,就换一个节点重拉槽表。
也就是说,客户端信任的是第一次拿到的拓扑,而不是实时负载。MOVED 重定向也只校验槽归属,不关心 CPU 或 QPS。所以即使节点 A 已经 95% CPU,只要它还持有槽 0–5460,请求就照打不误。
skip_full_coverage_check=True,防止槽表缺失导致路由错乱readonly_mode=True 自动读 slave,缓解主节点压力真正难处理的,是那些既没 bigkey、槽也均匀、hash tag 也合规,但因业务访问模式天然倾斜的情况——比如凌晨批量刷新全国城市天气数据,所有 key 都带 {city_id},而 top10 城市贡献了 60% 请求。这种得靠业务层限流或预热策略兜底,不是调槽位能解决的。