热点 Key 不是靠猜的:京东 HotKey 探测机制拆解

发布时间:2026/6/3 13:45:36

热点 Key 不是靠猜的:京东 HotKey 探测机制拆解 热点 Key 不是靠猜的京东 HotKey 探测机制拆解摘要Redis 热 key 真正麻烦的地方不是缓存本身而是热点往往来得很突然等慢日志、连接池、CPU 报警时已经晚了。本文结合京东云开发者社区的 JdHotkey 设计实践和京东零售开源仓库拆解 HotKey 探测的 client、worker、dashboard、etcd 四层结构讲清 Java 后端如何在缓存被打穿前发现热点 Key并把它转成本地缓存、限流、降级或熔断动作。1. 缓存被打穿之前谁先发现热点昨天我们聊过热点互动洪峰Bitmap 兜状态、Kafka 削峰、SDS 做计数快照、Caffeine Redis MySQL 做三级缓存、单飞锁合并 miss、限流退避防重建风暴。但那篇文章默认了一个前提系统已经知道哪个 key 是热点。真实线上系统里这一步更难。热点可能来自秒杀商品、活动页、直播间、爬虫用户、恶意 IP也可能来自某个接口突然被集中访问。你事后看 Redis 慢日志、连接池、CPU、DB 回源量当然能知道出事了问题是那已经晚了。所以今天只讲一个问题热点 Key 到底是怎么被系统实时发现的京东开源过一个热 key 探测框架JdHotkey。京东云开发者社区的文章里说它用于处理突发热点数据、热点用户、热点接口等场景核心目标是在较短时间内识别热 key并把它推送到服务端 JVM 内存里。Gitee 上的jd-platform-opensource/hotkey仓库也把它定位为“毫秒级探测热点数据毫秒级推送至服务器集群内存”的框架。它最值得学的不是“又加一层缓存”而是把热点治理拆成了两步第一步实时发现哪个 key 热了。 第二步热 key 进 JVM本地缓存、限流、降级或熔断。2. 普通缓存为什么发现不了热点常见 cache-aside请求进来 - 查 Redis - 命中返回 - 未命中查数据库 - 回写 RedisRedis 官方 cache-aside 文档也把它作为读多写少场景的常见解法应用先查 Redismiss 后回源主库再把结果写回缓存。问题是普通 cache-aside 是被动的。它只回答“这个请求是否命中缓存”回答不了“这个 key 是否正在变成热点”。比如/sku/query 整体 5 万 QPS 其中 skuId10086 这一个 key 占 3 万 QPS接口整体 QPS 看起来正常Redis 集群平均负载也可能看起来还行。但这个 key 如果落在某个 Redis 分片上那一个分片会先被打满。热点 Key 的麻烦在于平均 QPS 会掩盖单 key 峰值。Redis 命中率高不代表没有单分片热点。慢日志、CPU、连接池报警通常太晚。人工配置本地缓存跟不上突发流量。所以热点治理的第一步不是缓存而是探测。3. 京东 HotKey 的一句话理解一句话客户端采集访问 keyworker 聚合判断热度dashboard 配规则etcd 同步规则和热 key客户端拿到热 key 后放进 JVM 内存再由业务决定缓存、限流、降级或熔断。它不是 Redis 插件也不是改造 Redis 客户端。京东云文章里明确提到JdHotkey 不依赖 Redis本质是一个独立的热 key 探测系统。它关心的是“key 字符串”类型key 示例热商品skuId_10086热店铺shopId_888热接口/sku/query刷子用户userId_123恶意 IPip_1.2.3.4用户访问某商品userId_123:/sku/query:skuId_10086所以它不只服务缓存还能服务限流、风控、降级和热点统计。4. 四个组件client、worker、dashboard、etcd4.1 client嵌进 Java 应用的探测入口client 是业务应用里引入的 jar。它负责按规则收集待探测 key。定时批量上报给 worker。监听规则、worker 地址、热 key 变化。把探测出来的热 key 放入本地 Caffeine 缓存。对外提供“这个 key 是不是热 key”的判断能力。业务代码应该把它当成热点信号源if(JdHotKeyStore.isHotKey(key)){// 本地缓存、限流、降级、默认值等业务逻辑}注意HotKey 框架只负责告诉你 key 是否热不负责 value。比如 Redis 的sku:10086被判定为热 key 后value 仍然需要业务自己加载再放进本地缓存。4.2 worker真正做热度聚合和判断worker 是独立部署的 Java 程序。它负责从 etcd 读取规则。接收 client 批量上报的 key。在时间窗口内聚合 key 出现次数。达到规则阈值后判定为 hot key。把 hot key 推送给对应 APP 的客户端。worker 解决的是单机看不到全局热度的问题。集群 100 台机器 某个 sku 总访问 5000 次/秒 平均到每台机器只有 50 次/秒单机看起来不热整体其实已经很热。worker 聚合后才能在集群维度判断。4.3 dashboard规则和人工干预入口dashboard 是控制台。它负责配置规则。查看热 key。手工添加或删除热 key。规则示例skuId_ 开头1 秒超过 100 次算热 userId_ 开头2 秒超过 20 次算热 api_ 开头1 秒超过 1000 次算热商品、用户、接口、IP 的阈值不能混在一起。dashboard 的价值就是把业务规则从代码里拿出来。4.4 etcd配置中心和一致性通道etcd 主要负责存规则配置。存 worker 地址。存探测出来的 hot key。让 client、worker、dashboard 监听变化。这里需要的是配置同步、监听、注册发现和一致性通知。etcd 更适合做这种协调型工作。它保证一个 key 被判定为热 - 所有相关 client 都能收到 一个 key 被人工删除 - 所有相关 client 都能删除本地缓存5. 探测链路一个 key 怎么进 JVM 内存完整链路业务请求访问 key - client 判断这个 key 是否命中规则 - client 在本地窗口内累加 - 每 500ms 批量发送待测 key 到 worker - worker 按规则聚合计数 - 达到阈值判定为 hot key - worker 推送 hot key 给 client - client 放入本地 Caffeine - 业务后续访问走本地缓存、限流或降级几个关键点5.1 只有命中规则的 key 才上报不是所有字符串都应该上报。HotKey 探测不是日志系统不负责记录所有访问。它只负责尽快识别可能有风险的 key。5.2 client 先本地累加再批量发送京东文章里提到client 默认每 500ms 批量发送一次待测 key这段时间内client 先在本地收集并累加。这比每次访问都发给 worker 更合理网络请求少。先做局部聚合。worker 收到的是增量统计不是每一次访问。5.3 固定 key 发到固定 worker同一个 key 不能分散到多个 worker否则每个 worker 只看到局部计数。所以可以用hash(key) % workerCount固定 key 发送到固定 worker聚合才准确。5.4 热了之后不再反复上报一个 key 已经被识别为热 key后续重点就不是继续证明它热而是让客户端在本地处理它。继续上报只会浪费探测资源。6. HotKey 探测出来后Java 侧怎么处理JdHotkey 只回答这个 key 是否热后续动作由业务自己决定。6.1 本地 Caffeine 缓存Redis 热 key 最常见的处理方式是把 value 放进 JVM 本地缓存。Stringkeysku:skuId;if(JdHotKeyStore.isHotKey(key)){ProductproductlocalCache.getIfPresent(key);if(product!null){returnproduct;}productredis.get(key);JdHotKeyStore.smartSet(key,product);returnproduct;}returnredis.get(key);Caffeine 的配置文档支持maximumSize、expireAfterWrite、refreshAfterWrite等参数。所以本地缓存必须有容量、过期和刷新策略不能当无界 Map。6.2 限流或拒绝访问如果 key 代表刷子用户或恶意 IPuserId_123 ip_1.2.3.4热了之后不一定缓存而是限流、拒绝、返回默认响应。6.3 接口降级如果 key 代表某个接口api_/sku/query热了之后可以短时间返回简化结果、默认值或者关闭部分非核心计算。6.4 单飞锁合并首次回源HotKey 负责发现single-flight 负责合并第一次加载。CompletableFutureValuefutureinflight.computeIfAbsent(key,k-CompletableFuture.supplyAsync(()-loadValue(k),executor).whenComplete((v,ex)-inflight.remove(k)));returnfuture.join();否则热 key 第一次进本地缓存前多个线程仍可能一起回源。7. 它和普通二级缓存有什么区别很多人会问这不就是 JVM 本地缓存吗不是。二级缓存只是结果HotKey 探测才是前置机制。对比点普通二级缓存HotKey 探测是否知道 key 热不热不知道通常无脑缓存实时统计后判断是否集群一致常常不一致通过 etcd 推送保持一致是否能处理刷子用户不适合可以把用户 ID 当 key是否能处理热接口不适合可以把接口路径当 key是否需要业务规则较少规则是核心主要价值减少部分 Redis/DB 访问在热点刚出现时快速保护系统8. 面试怎么讲京东 HotKey如果面试官问Redis 热 key 怎么处理不要只说“加本地缓存”。可以这样讲我会把热 key 问题拆成两步。 第一步是发现不能靠人工猜也不能只看接口平均 QPS。 可以参考京东 HotKey 的思路在业务 client 侧采集待测 key按规则批量上报 worker由 worker 在时间窗口内聚合判断达到阈值后通过 etcd 推送给所有客户端。 第二步是处理客户端拿到 hot key 后可以把 value 放进 Caffeine 本地缓存也可以对用户、IP、接口做限流、降级或熔断。对于首次加载还要配合单飞锁避免多个线程同时回源。这段回答比“Redis 热 key 放本地缓存”更完整因为它讲清楚了发现、聚合、规则、通知和处理动作。9. Java 后端落地清单如果你不是直接引入 JdHotkey而是自己做简化版也要保留这些关键点先定义 key 维度商品、用户、接口、IP、组合维度。规则必须可配置不同前缀、不同业务使用不同阈值。client 不全量上报先按规则过滤再本地聚合再批量发送。同一个 key 固定打到同一个 worker否则聚合不准。worker 做窗口统计目标是快速判断不是精确日志。热 key 要推送到全部相关 client否则集群行为不一致。本地缓存要有容量和过期Caffeine 不能当无界 Map。热 key 只是一种信号后续动作可以是缓存、限流、降级、熔断、默认值。首次加载要防并发回源HotKey 单飞锁一起用。监控探测系统本身worker QPS、队列长度、推送延迟、hot key 数量都要看。总结热点 Key 不是靠猜的。真正可上线的高并发缓存系统不能只在 Redis 被打穿之后再补救而要在热点刚出现时就发现它。京东 HotKey 的机制可以概括成client 采集 key worker 聚合判断 dashboard 配规则 etcd 同步推送 client 本地保护这套机制背后的工程判断很清楚缓存治理不是从“缓存什么”开始而是从“如何发现危险的 key”开始。参考资料京东云开发者社区京东毫秒级热 key 探测框架设计与实践已完美支撑 618 大促https://developer.jdcloud.com/article/2855Gitee京东零售 hotkey 开源仓库https://gitee.com/jd-platform-opensource/hotkeyRedis 官方文档Cache-asidehttps://redis.io/docs/latest/develop/use-cases/cache-aside/Caffeine WikiSpecificationhttps://github.com/ben-manes/caffeine/wiki/Specification

相关新闻