Hash Tag会导致数据倾斜,因为Redis集群仅对{}内内容计算CRC,使相同tag的key全落入同一slot;如user:{1001}:profile与user:{1001}:settings均落在同一节点,造成单点负载过高。

为什么 Hash Tag 会导致数据倾斜
Redis 集群用 CRC16(key) % 16384 算出 slot,但只要 key 里含 {...},就只对花括号内内容做 CRC 计算。如果业务大量使用类似 user:{1001}:profile、user:{1001}:settings 这种结构,所有以 {1001} 为 tag 的 key 全部落到同一个 slot,进而打到同一节点——哪怕你有 6 个 master,1001 用户相关的所有数据都挤在一台机器上。
更麻烦的是,tag 内容离散度差(比如固定前缀 + 自增 ID),或业务误用 {common} 这类全局 tag,会直接让成千上万个 key 堆进一个 slot。
如何定位哪些 tag 在搞鬼
先确认是否真由 tag 引发倾斜:用 redis-cli -c -h {host} -p {port} cluster countkeysinslot {slot} 扫描 slot 键数,找出明显高于平均值(比如 > 5 倍)的 slot;再用 cluster getkeysinslot {slot} 100 拉一批 key 出来,肉眼观察共性。
常见危险模式包括:
-
order:{202404}:list(月粒度 tag,当月所有订单全进一个 slot) -
cache:{global}(硬编码统一 tag) -
log:{error}(错误日志全走一个 tag) -
user:{1}:xxx、user:{2}:xxx…但{1}到{100}都是热用户,且没做分片扩展
改写 tag 的三个实操原则
不是删掉 {} 就行,得保证新 key 仍能被业务逻辑正确识别和聚合。推荐做法:
- 把高基数字段放进 tag,比如把
user:{uid}:orders改成user:{uid % 100}:orders:{uid},用取模制造 100 个子槽位 - 用业务自然分片字段替代硬编码,比如订单 key 从
order:{202404}改为order:{shard_id}:202404{seq},其中shard_id来自订单号哈希或用户 ID 分段 - 对已存在的热 tag key,不要直接 rename,而是双写过渡:新逻辑写带分散 tag 的 key,读逻辑先查新 key,未命中再查旧 key,灰度完成后下线旧路径
注意:redis-cli --bigkeys 只能发现大 key,不能暴露 tag 倾斜;必须结合 countkeysinslot + 抽样分析才能准确定位。
上线前必须验证的两件事
改完 tag 后别急着全量,先做两件事验证分布效果:
- 用线上流量镜像或构造等比例测试数据,跑一遍新 key 生成逻辑,统计生成的 slot 分布(可用
python -c "print(crc16('key') % 16384)"快速验证) - 在预发集群中部署新逻辑,用
redis-cli -c cluster info观察各节点 keys 数是否收敛到 ±5% 以内,同时检查memory_used_human是否同步均衡
最容易被忽略的是:客户端 SDK 如果缓存了 slot 映射关系(比如 JedisCluster 的 refreshNodeInfo 默认不自动触发),改 key 规则后可能仍按旧映射路由,导致请求错发——务必确认客户端已刷新槽映射或重启连接池。











