Redis 分片后热点仍集中,问题更可能出在哪一层?
Redis 分片能摊开平均压力,却不会顺手打散爆款对象、集中过期和回源窗口。分片做完热点还扎堆时,更该回头检查 key 设计、访问形态、本地缓存和失效节奏,而不是一上来都算到槽位分布头上。
很多 Redis 热点治理会先走到分片这一步,这个方向没错,但分片上线后如果现场还是这样:
- 少数节点在高峰时持续偏热
- 热门接口 RT 还是一阵一阵抖
- 热 key 没消失,只是从单机问题变成集群里的局部问题
- 数据库回源、连接池等待也开始跟着升
团队很容易立刻去盯槽位、迁移和 hash 分布。可真实线上里,更常见的情况是:平均承载已经摊开了,热点语义却一点没变。请求还是围着那几十个对象打,过期时间还是扎堆,本地缓存还是没兜住,最后热点当然还会继续集中。
所以这篇文章想讲清楚的不是“分片有没有价值”,而是另一个更常把人带偏的问题:
Redis 已经分片,为什么热点还是没散开?这时更该怀疑分片实现,还是先回到 key 设计、访问模式和失效策略看热点究竟卡在哪一层?
热点还在时,先分清落点
分片做完以后还在偏热,很多团队会立刻把注意力放到槽位和迁移细节上。但比追着实现细节更重要的,是把现场信号分粗:热点到底卡在单 key、对象族群,还是失效窗口。
| 你现在看到的现象 | 建议先把排查落到哪一层 | 为什么 |
|---|---|---|
| 已经做了 Redis 分片,但热点仍集中在少数节点或少数对象上 | 看完本文再定下一步 | 这里先帮你判断热点究竟停在 key、对象族群还是失效窗口 |
| 你还没确认是不是热 key、热点族群或失效窗口 | 转去热 key 治理页 | 热点类型分清,后面的动作才不会跑偏 |
| 你在处理的是大面积回源、TTL 集中过期 | 转去回源放大页或 TTL 页 | 这时更像 miss 窗口把后半段等待链一起带起来了 |
| 你想讨论 Redis 集群部署实现和迁槽细节 | 把本文当成前置判断 | 先确认问题是不是确实落在分片层,再深入实现细节 |
这种现场为什么别急着查分片实现
如果分片已经做完,但热点还持续扎堆,就可以直接按下面这个顺序往下收。
它想纠正的误判是:分片能摊开平均负载,却不会自动改写热点语义。所以当热点还在时,更值得回头看 业务对象、key 设计、本地缓存和失效策略,确认热点有没有真的被打散,而不是直接把锅扣到集群实现上。
先把热点收成 5 个判断
- 把热点分成点状热点、热点族群和窗口型热点三类。
- 再看热点是不是本来就高度绑定少数业务对象,而不是 hash 槽不均。
- 再看热点 key 有没有本地缓存、边缘缓存或失效节奏保护。
- 如果数据库回源、连接池和接口 RT 也在变差,把后半段等待链一起带上。
- 只有当前几层都解释不住,再优先查分片实现本身。
一、先纠正一个预期:分片解决的是“平均压力”,不是“热点语义”
这是理解这类问题的起点。
Redis 分片通常能直接解决的是:
- 总容量不够
- 总 QPS 太高
- 单机内存和网络带宽到瓶颈
- 平均请求分布需要更大的承载面
但它默认没替你做的事包括:
- 把单个热 key 变成多个 key
- 把业务上的超级热点对象打散
- 把热点失效时间错开
- 把每次请求都打 Redis 的模式自动改成本地兜底
也就是说,分片更多是在回答:
当请求已经足够分散时,系统能不能横向承接更多平均负载?
它没有自动回答:
为什么请求会过度集中到少数对象上?
所以如果你把“分片后热点应该自然消失”当作默认预期,后面看到热点还在,就会觉得是系统出了反直觉的毛病。其实不是,预期本身就错了。
二、分片后热点仍集中,最常见的 4 类真实原因
1. 热点本来就是单 key 级别,分片只是在扩大背景,不会打散这个 key
这是最常见的一类。
比如:
- 爆款商品详情
- 首页活动配置
- 排行榜结果
- 某个全局开关
- 某个超级热门用户主页
这类热点的本质是:
- 所有请求都在打同一个 key
- 分片再多,这个 key 还是只能落在某一个节点或某一个槽位
所以你会看到:
- 整个集群总容量更大了
- 但单个热 key 仍然把局部节点打偏
- 背景流量被分散后,这个节点反而显得更突出了
这类问题不是分片失败,而是热点仍然停留在 key 级别。
2. 热点不是单 key,而是单一业务对象族群仍然过于集中
有时热点看起来不是一个 key,而是一小批 key。
例如:
- 活动页上的几十个爆款商品
- 某个租户下的热门资源集合
- 某个榜单页不断刷新的 top N 数据
- 同一地区、同一频道、同一业务入口下的热点内容
这时即使分片后:
- key 数量变多了
- 总体分布也许比以前好一点
但如果这些 key:
- 命名规则相近
- 请求时间窗高度重合
- 失效时间也相近
那么热点仍然会集中在少数节点、少数时段。
这里的问题本质上在业务流量结构,不在“有没有分片”。
3. 热点并不是读热点本身,而是“失效后的回源热点”
这类特别容易误判。
表面上你看到的是:
- 某些 Redis 节点在高峰时特别忙
- 热门接口 RT 也在抖
- 但更严重的时候数据库回源和连接池也一起变差
继续查会发现:
- 热点 key 失效窗口太集中
- 本地缓存没有兜住
- Redis miss 以后大量请求一起回源
- 数据库回填慢,又把下一波热点继续放大
这时真正的问题不只是“Redis 热点还在”,而是:
分片没有解决热点失效和重建模式,热点在 miss 后直接转成了数据库等待链。
4. 热点实际上被应用实例本地行为重新集中了一次
很多团队只看 Redis 节点,不看应用层访问模式。
但真实项目里,热点还可能在应用侧被重新放大:
- 本地缓存没有做,所有实例都直接打 Redis
- 本地缓存 TTL 太短,实例群一起回 Redis
- 某批实例因为发布、重启、缓存预热缺失,先于其他实例 miss
- 某些流量入口把更多请求集中路由到少数实例,再集中打某批 Redis key
这种场景下,你看到的是 Redis 热点,但根子有一部分在应用层的访问形态。
三、先别一上来查 hash 槽:先判断热点到底是“点状”还是“簇状”
如果以后再遇到“分片后热点还在”,第一步我更建议先问:
热点是集中在一个 key,还是一簇 key,还是一整类失效窗口?
1. 点状热点
信号通常是:
- 少数 key 命中次数远高于其他 key
- 节点负载不均明显,但热点对象很容易数出来
- 热门接口与几个固定业务对象高度相关
这类更像典型热 key 问题。
2. 簇状热点
信号通常是:
- 不是单 key,而是一批 key 一起热
- 同一个业务页、同一个榜单、同一个活动入口相关 key 同时被打
- 节点偏热与某类业务前缀或某批对象集合对应明显
这类更像业务对象族群和流量入口问题。
3. 窗口型热点
信号通常是:
- 某些时间点 Redis 和数据库一起抖
- 热点更依赖失效时间、预热时间、发布窗口
- 命中率与 miss QPS 在短时间窗口内尖刺很明显
这类更像 TTL、预热、逻辑过期和重建策略问题。
只有先分清这三类,后面才知道该查哪一层。
四、排查顺序别倒过来:先业务热点,再失效策略,最后分片实现
很多人默认会先查:
- 槽位分布
- 节点数
- hash 算法
- 集群扩容是否做均衡
这些方向不是不该查,但通常不该排在第一位。更贴近现场的顺序往往是下面这样。
1. 热点对象是不是本来就高度集中
重点问:
- 慢接口是不是高度集中在少数业务对象
- 热点 key 是否容易枚举出来
- 最近活动、推荐、入口流量是否刚把一批对象打成超热点
- 热点对象在分片前后是否其实没变,只是背景流量变了
如果答案是“是”,问题更可能在业务热点,而不是集群实现。
2. 再看热点 key 是否有本地缓存或边缘兜底
如果每个请求都要直打 Redis:
- 热点 key 很容易持续发热
- 分片只能降低别的 key 对它的干扰
- 无法降低它自身被访问的频率
这里特别要看:
- 热点配置、榜单、公共字典是否可以本地缓存
- 应用实例有没有短 TTL 本地兜底
- 逻辑过期或后台刷新有没有做
很多热点问题真正缺的不是更多 Redis 节点,而是少打一层 Redis。
3. 再看热点失效窗口是否集中
重点看:
- 热点 key 是否在同一时间段一起过期
- 预热是否做了,但热点集合和真实流量并不匹配
- 某批实例重启后是否让热 key 回源集中发生
- 热点 key 是否采用了互斥重建、逻辑过期或异步刷新
如果这里有问题,分片后热点依然会在某几个窗口里爆出来。
4. 再看 key 设计有没有把“热点族群”放在同一类模式里
这一步不是说 Redis 哈希槽一定有问题,而是要看:
- 业务键是否天然集中在一批对象上
- 某类 key 是否命名前缀不同,但语义上总被一起访问
- 一批热门对象是否总在同一页面、同一接口批量读取
如果请求总是成组访问同一批 key,热点就不再是单 key 问题,而是业务读模式问题。
5. 最后再查分片实现本身
到了这一步,再看:
- 槽位是否均匀
- 节点是否有异常偏载
- 扩容迁移是否残留问题
- 客户端路由、pipeline、连接管理是否有局部问题
这一步当然重要,但它更适合作为排除法的后段,而不是默认起点。
五、为什么很多“分片后热点仍集中”,最后会演变成数据库和接口一起慢
这类问题最麻烦的,不是 Redis 节点偏热本身,而是热点一旦失效、抖动或超时,后面很容易顺着缓存故障链继续传导:
Redis 热点集中 -> 热点 key miss 或超时 -> 大量请求回源数据库 -> 回填变慢、连接池变紧 -> 线程池和接口 RT 一起变差
所以现场常常不是只有:
- 某个 Redis 节点忙
而是同时出现:
- 热门接口抖
- 数据库回源升
- 连接池 pending 上来
- 上游重试继续放大
如果走到这一步,说明真正该看的已经不只是分片,而是整条“热 key -> miss -> 回源 -> 等待”的链路。
六、一个典型案例:为什么商品详情做了 Redis 分片,爆款还是把系统拖抖了
假设某个商品详情系统做了下面这些治理:
- Redis 从单机切成分片集群
- 热门商品详情都走缓存
- 总体 QPS 承载能力明显提升
平时看起来都不错,但大促一来仍然出现:
- 某些节点明显更热
- 爆款详情接口 P99 明显升高
- 数据库商品详情查询量也一起抬头
第一眼容易怎么判断
很多人会说:
- 分片没打散
- 扩容还不够
- Redis 集群某些节点太弱
继续往下查
会发现:
- 真正超热点的商品就那几十个
- 这些商品详情 key 虽然分散在不同槽位,但每个 key 都足够热
- 应用层没有本地缓存,所有请求都直接打 Redis
- 热点 key TTL 比较短,且一批爆款的过期时间很接近
- 一旦有几组 key 同时进入重建窗口,数据库回填也会被拉高
结论
这时问题并不是“分片无效”,而是:
- 分片只解决了平均承载
- 没解决超级热点 key 本身
- 没解决热点失效窗口
- 没解决本地访问模式
这个案例很典型,也最能说明为什么这类问题不该默认从分片实现层开始查。
七、关键误判:这类问题最容易在哪些地方走偏
误判 1:分片后热点还在,就说明分片方案失败
不一定。
更常见的是热点语义根本没变,分片本来就没负责解决这部分。
误判 2:看到节点负载不均,就立刻扩容
如果是少数热 key 或热点失效窗口问题,扩容最多降低背景噪声,不会根治热点。
误判 3:只查 Redis,不看数据库回源和接口等待
很多热点问题真正伤系统的,是 miss 后半段的等待链,而不是节点偏热截图本身。
误判 4:把所有热点都归因于 hash 分布
业务对象集中、接口入口集中、失效策略集中,本身就足以制造热点,不需要 hash 层先出问题。
误判 5:有了 Redis 集群,就不再考虑本地缓存和逻辑过期
恰恰相反,越是超热点对象,越需要多层吸收,而不是把所有请求都直打 Redis。
八、FAQ:分片已经做了,现场通常还会追问什么
1. 分片是不是只能解决平均负载,不能解决热 key?
基本可以这么理解。分片更擅长横向扩平均承载,不会自动把单个热 key 打散。
2. 这类问题默认先查集群,还是先查业务热点?
默认更建议先查业务热点、key 访问结构和失效窗口,再排分片实现本身。因为前者更高频,也更容易被忽略。
3. 热点 key 做本地缓存,会不会带来一致性问题?
会带来权衡,但对配置、榜单、热点详情这类可接受短时间最终一致的数据,通常比持续直打 Redis 更稳。
4. 分片后节点负载不均一定是坏事吗?
不一定。
如果业务热点本来就偏斜,局部节点更忙并不奇怪。关键要看这种偏斜是否已经把 RT、miss 和数据库回源一起拖坏。
九、接下来通常会顺手查这几件事
如果已经确认做过分片,但热点还是砸在少数节点上,后面一般不会只盯着 hash 槽看,而是把下面几条线一起补齐。
- 已经确认是少数热 key 特别集中,可以接着看 Redis 热 key 问题怎么发现和处理?常见思路讲清楚,把单 key 热点拆开
- 热点更依赖失效时间和回源窗口,可以对照 缓存 TTL 设计不当,为什么会把高峰流量集中打回数据库? 和 缓存回源流量突然放大时,到底先查业务热点、命中率还是数据库压力?
- 预热已经做了,但流量切换时还是抖,可以补看 缓存预热做了还是抖,先查预热范围还是流量切换?
- Redis 偏热已经把数据库、连接池和接口 RT 一起拖慢,可以一起参考 缓存层出问题后,为什么应用和数据库会一起变慢的等待型链路?、接口响应慢怎么排查?后端性能问题定位步骤 和 数据库连接池打满时,根因通常不是连接数太小
- 如果只是想把运行态观察面补齐,再看 Spring Boot Actuator、线程池、连接池、慢请求这些运行态观测点,应该怎么串起来看?
十、最后总结:分片把路修宽了,不等于热点自己会散
这类现场最容易误判的地方,是把“已经分片”直接等同于“热点应该平均”。可分片主要解决的是平均承载,不会替你改写爆款对象、集中过期和本地未命中的访问结构。
所以更有用的收口方式是:先看热点到底是单 key、对象族群还是失效窗口,再分别回到 key 设计、本地缓存、TTL 和回源重建策略;只有这些都解释不住,才把怀疑重点压到分片实现本身。这样看问题,后面的治理动作才会更具体,也更容易真正把热点压下去。