大 Key / 热 Key 排查与拆分

一句话总结

大 Key 是”量”的问题——一个 key 塞了太多数据,读写都慢,拖死整个 Redis。热 Key 是”频”的问题——一个 key 被太多人同时抢着读,单节点 CPU 被打满。大 key 靠拆,热 key 靠散。


🌰 大 Key 篇:胖子是怎么拖死 Redis 的

你是 Redis,你是单线程

 
想象你是奶茶店唯一的店员(Redis 是单线程的)。
 
正常情况:
 
  顾客 A:"我要一杯奶茶"
 
  你:3 秒做好 → A 走了
 
  顾客 B:"我要一杯柠檬茶"
 
  你:3 秒做好 → B 走了
 
  排队的人虽然多,但每个人 3 秒,不堵。
 
大 Key 来了:
 
  顾客 C:"把我这 500 斤大米搬进店里"
 
  你:???
 
  你花了 5 分钟搬米(操作大 key)
 
  这 5 分钟里,后面排了 100 个顾客
 
  每个顾客都在等你搬完米才能点单
 
  100 个人等 5 分钟 → 全跑了 ❌
 
这就是大 key 的问题:
 
  Redis 是单线程,一次只处理一个请求。
 
  你的请求耗时 5 分钟,
 
  后面所有请求都得等着。
 

为什么会有大 Key?

 
常见的大 key 是怎么产生的:
 
① 日志全塞一个 List
 
  业务代码:r.lpush("logs", new_log)
 
  你觉得:就存个日志嘛,能有多大?
 
  一年后:logs 这个 List 里面有 5 亿条数据
 
  任何一次操作 logs → Redis 卡死 ❌
 
② 把 MySQL 一张表全塞进一个 String
 
  业务代码:json.dumps(全量用户数据) → r.set("all_users", 大json)
 
  一个 key 占了 2GB 内存
 
  每次读它 → Redis 要返回 2GB 数据 → 网络打满 ❌
 
③ Hash 存了太多字段
 
  一个 Hash 存了 1000 万个粉丝关系
 
  查询 / 遍历 / 删除 → 全部巨慢 ❌
 

大 Key 的危害——一个具体的故事

 
你的系统里有一个 key 叫 "user:100:fans",
 
是个 Hash,存了 200 万个粉丝 ID。
 
这天你干了一件事:
 
  HDEL user:100:fans some_fan
 
  → 删一个粉丝而已,应该很快吧?
 
但你在监控上看到:
 
  Redis 的延迟从 1ms 飙到了 500ms
 
  这个 HDEL 命令执行了 200ms
 
  (因为 Hash 底层要维护大字典的 rehash)
 
这 200ms 里:
 
  99 个其他请求被堵住了
 
  其中有 10 个是用户下单的请求
 
  用户点了下单按钮 → 转圈 200ms → 生气的走了 ❌
 
第二天又干了一件事:
 
  你想看看这个 Hash 有多大
 
  执行了 HLEN user:100:fans → 50ms
 
  执行了 HGETALL user:100:fans → 直接 3 秒,超时了
 

怎么发现大 Key?

 
你在线上发现 Redis 突然变慢了。
 
怎么确定是不是大 key 搞的?——三招:
 
第一招:看慢查询日志
 
  SLOWLOG GET 10
 
  如果看到:
 
    "HDEL user:100:fans" 耗时 200ms
 
    "LRANGE logs 0 -1" 耗时 1500ms
 
    "DEL big_set" 耗时 3000ms
 
  → 八成是遇上大 key 了
 
第二招:redis-cli --bigkeys(最常用)
 
  终端敲一行命令:
 
    redis-cli --bigkeys
 
  它会告诉你:
 
    Biggest String  found 'user:100:profile'  → 2.5 MB
 
    Biggest Hash    found 'user:100:fans'     → 200万字段
 
    Biggest List    found 'logs'              → 5000万条
 
第三招:MEMORY USAGE 精确查
 
  MEMORY USAGE user:100:fans
 
  → 52428800(50MB)
 
  一个大 key 占了 50MB,别的 key 才几百字节
 

大 Key 怎么办?——拆!拆!拆!

 
核心思路就一个字:拆。
 
怎么拆看具体场景:
 
场景 1:日志 List 太大了(5000 万条)
 
  不改代码不行。
 
  原来:一个 logs 里塞了所有日志
 
  改成:logs:2026-07-01, logs:2026-07-02, ...
 
  每天一个 key,7 天前的自动淘汰。
 
  效果:最大 key 从 5000 万条变成 100 万条。
 
场景 2:粉丝 Hash 太大了(200 万粉丝)
 
  把一个大 Hash 拆成 200 个小 Hash。
 
  粉丝 ID 对 200 取模,决定去哪个小 Hash。
 
  原来是:
 
    user:100:fans → {fid1: name1, ..., fid200万: name200万}
 
  改成:
 
    user:100:fans:0 → {fid1: name1, ..., fid1万: name1万}
 
    user:100:fans:1 → {fid1万+1: ...}
 
    ...
 
    user:100:fans:199 → {...}
 
  查某个粉丝时先算槽位:
 
    slot = fan_id % 200
 
    HGET user:100:fans:{slot} fan_id
 
  每个小 Hash 只有 1 万条,快得很。
 
场景 3:大 String 太大了(存了个 10MB 的 JSON)
 
  两个方向:
 
  ① 拆字段——不要把整个对象序列化成一个 String
 
     改成用 Hash,一个字段存一个属性
 
     你不需要所有字段时,只读需要的字段就快多了
 
  ② 压缩——非要用 String,就先压缩再存
 
     10MB 的 JSON → GZIP 压缩 → 剩 1~2MB
 
     读的时候解压回来
 
场景 4:冷热分离——不是所有数据都要在 Redis 里
 
  3 年前的订单也放 Redis?
 
  一个月前就不查的数据也放 Redis?
 
  做法:Redis 只存最近 7 天的热数据
 
        超过 7 天的自动淘汰或搬到磁盘
 
        用户查历史数据 → 走 MySQL
 
        不占 Redis 内存,也没有大 key 的问题
 

🌰 热 Key 篇:顶流是怎么打爆 Redis 的

一个真实的故事

 
双十一零点,某电商网站。
 
商品 "iPhone18" 的库存 key:
 
  "stock:iphone18" → 值就是库存数字
 
平时:
 
  一秒钟几百个人点进来看看,Redis 轻松处理。
 
双十一零点一秒:
 
  10 万人同时点抢购
 
  全部去读 "stock:iphone18" 这个 key
 
出事了:
 
  这个 key 所在的 Redis 节点 CPU 瞬间 100%
 
  其他 key(用户登录、购物车、订单…)也在这个节点上
 
  但它们全堵住了,因为 CPU 在忙着处理 "stock:iphone18"
 
  你看到监控:
 
    这个节点的延迟:1ms → 5000ms
 
    其他节点:正常
 
    → "集群倾斜"——一个人忙死,其他人闲死
 
这就是热 key 的问题:
 
  不是这个 key 有多大(就是一个数字),
 
  是太多人同时抢着读它。
 
  Redis 单线程处理不过来。
 

热 Key 怎么发现?

 
三招,从简单到复杂:
 
第一招:redis-cli --hotkeys
 
  终端敲一下 → 看哪些 key 访问频率最高
 
  前提是你配置了 LFU 淘汰策略
 
第二招:客户端埋点统计
 
  在你的代码里,每次操作 Redis 之前,
 
  往本地计数器里 +1。
 
  每分钟打一次日志,看哪些 key 访问最多。
 
  土办法,但很实用。
 
第三招:看 Redis 的 CPU 和延迟监控
 
  如果某个 Redis 节点的 CPU 是其他节点的 5 倍
 
  → 这个节点上大概率有热 key
 

热 Key 怎么办?——就一个字:散

 
核心思路:把流量分散到多个 Redis 节点上。
 
方案 1:本地缓存(最有效)
 
  你的应用服务器自己存一份热 key 的数据。
 
  比如 stock:iphone18 = 99,你缓存到本地内存里,
 
  过期时间设 1 秒。
 
  效果:
 
    10 万请求 → 9.9 万直接从本地内存拿
 
    只有 1000 个请求穿透到 Redis
 
  相当于每个应用服务器自己挡掉 99% 的流量。
 
  Redis 的压力直接降到 1% ✅
 
方案 2:把 key 散成多个副本
 
  热 key 之所以热,是因为所有请求都打在同一个槽上。
 
  那我们把 key 复制几份,分散到不同槽:
 
  原来:
 
    所有客户端读 "stock:iphone18" → 同一个节点
 
  改成:
 
    客户端写入时写到 10 个 key:
 
      "stock:iphone18:0"、"stock:iphone18:1"、...
 
    客户端读的时候随机读一个:
 
      index = random(0, 9)
 
      GET "stock:iphone18:{index}"
 
  流量从 1 个节点分散到 10 个节点。
 
  代价:数据有短暂不一致(一个 key 更新了,另一个还没更新)
 
  适用:秒杀场景下可以接受 1 秒内的不一致
 
方案 3:读写分离 + 多从库读
 
  主库写,三个从库读。
 
  热 key 的读请求分散到三个从库。
 
  每个从库只扛 1/3 的流量。
 
方案 4:限流——最后防线
 
  上面方案都用了,流量还是大到打穿 Redis。
 
  那就只能限流了——每秒最多处理 10 万次请求,
 
  超出的直接返回"稍后重试"。
 
  宁可拒绝一部分用户,也不能让 Redis 被打挂。
 
  因为 Redis 一挂,所有用户都用不了了。
 

一句话讲清

 
延伸提问:"你线上遇到过 Redis 大 key 或者热 key 吗?"
 
你:
 
"遇到过。之前项目里有个日志 List 堆积了 5000 万条,
 
一次 LRANGE 操作把 Redis 卡了好几秒。
 
排查是先看慢查询日志,发现有几个 DEL 和 LRANGE 特别慢,
 
然后执行 redis-cli --bigkeys 确认,
 
发现这个 logs key 是最大的。
 
解决是按天拆分,改成 logs:yyyy-mm-dd 的格式,
 
加上过期时间自动淘汰。之后监控就没再出现过。
 
热 key 的话,
 
我们之前做活动的时候有个商品被大量用户抢购,
 
那个库存 key 一秒钟被读十几万次,
 
导致它所在的 Redis 节点 CPU 打满。
 
也是从监控发现其他节点正常,就这个节点异常。
 
解决是加了一层本地缓存,
 
把数据在应用内存里缓存 1 秒,
 
90% 的请求被本地挡住了,
 
Redis 的压力基本解除了。"
 

记忆口诀

大 key 是”量”——一个 key 塞太多,单线程下操作它等于堵住所有人。拆成小份就好了:按时间拆、按哈希拆、冷热分离。

热 key 是”频”——太多人同时读同一个 key。把流量散开就好了:本地缓存扛 99%、散列成多个副本、多从库分摊读。

发现大 key → —bigkeys + 慢查询。发现热 key → —hotkeys + 客户端统计。

核心思想:大 key 要拆,热 key 要散。

速记卡(面试闪卡)

Q1:一句话讲清「大 Key / 热 Key 排查与拆分」到底是什么?

A:大 Key 是一个 key 塞太多数据拖慢单线程 Redis,热 Key 是一个 key 被太多人同时读打满 CPU;大 Key 拆、热 Key 散。

Q2:大 Key 怎么拖死 Redis? —— 怎么理解?

A:像奶茶店唯一店员被叫去搬 500 斤大米——Redis 单线程一次只处理一个请求,操作大 key(如 5000 万条日志 List)要几分钟,后面 100 个顾客全干等跑光。大 Key 是”量”的问题。

Q3:怎么发现大 Key? —— 怎么理解?

A:像查哪个包裹超重——三招:① SLOWLOG GET 看慢命令(HDEL/LRANGE/DEL 耗时异常);② redis-cli —bigkeys 扫出最大 String/Hash/List;③ MEMORY USAGE key 精确看占用字节。

Q4:热 Key 又是怎么回事? —— 怎么理解?

A:像双十一抢 iPhone 库存——10 万人同一秒读 stock:iphone18,它所在节点 CPU 瞬间 100%,其他 key 全堵(集群倾斜)。热 Key 是”频”的问题,不是大,是太多人同时抢读同一个。

Q5:大 Key 和热 Key 怎么治? —— 怎么理解?

A:大 Key 像把大货拆小件——按天拆日志(logs:yyyy-mm-dd)、Hash 按 id 取模拆 200 份、大 String 拆字段或压缩、冷热分离只留 7 天热数据。热 Key 像把人流散开——本地缓存扛 99%、复制成多副本随机读、多从库分摊、限流兜底。

Q6:核心速记主线有哪些?

  • 大Key是”量”:一个key塞太多,单线程下操作它等于堵住所有人

  • 大Key发现:SLOWLOG慢日志 + redis-cli —bigkeys + MEMORY USAGE

  • 热Key是”频”:太多人同时读同一key,节点CPU打满(集群倾斜)

  • 大Key要拆(按时间/哈希/冷热分离),热Key要散(本地缓存/多副本/多从库/限流)

口诀

A:大Key是胖子,一人搬米堵全店。

热Key是顶流,万人抢读CPU爆。

大Key靠拆小份,按天按模冷热分。

热Key靠散开,本地缓存扛九成。

相关链接