Redis 响应偶尔卡顿、客户端莫名超时,可内存没满、CPU 也不高——九成是某个「大 key」在作怪。这里说的大 key,指单个 value 超过约 1MB 的 String,或元素超过一万的 Hash / List / Set / ZSet。它不触发 OOM,却会在删除、序列化、主从同步时把单线程的 Redis 堵住几秒。下面用线上能直接敲的命令,一步步把它揪出来。
集合 > 1 万
–memkeys
别用 DEL
加分片
一、先确认是不是大 key 的锅
大 key 的征兆很隐蔽,先拿三个命令把疑点坐实:
- 慢日志:redis-cli SLOWLOG GET 20,盯住 KEYS *、HGETALL 全量、LRANGE 0 -1 这类「一把梭」命令,它们往往是大 key 的帮凶。
- 延迟基线:redis-cli –latency -i 1,正常应低于 1ms;如果突然飙到几毫秒甚至几十毫秒,并伴随客户端超时,疑点成立。
- 内存曲线:INFO memory 看 used_memory_human 是否大起大落——删大 key 时内存会一次性回收,曲线会出现断崖。
看慢日志
→
扫 bigkeys
→
定位 key
→
UNLINK/拆分
二、用 –bigkeys 扫出元凶(别在生产裸跑)
官方给了现成工具,低峰期加限速参数跑:
redis-cli -h 127.0.0.1 -p 6379 --bigkeys
# 输出示例:
# Biggest string found 'user:feed:888' has 52428800 bytes
# Biggest list found 'task:queue' has 2500000 items
一定要加 -i 0.1 限速:每扫 100 个 key 休眠 0.1 秒,避免 ops 飙升触发报警。Redis 7+ 还能用更准的 –memkeys 按内存采样:
redis-cli --memkeys --memkeys-samples 100
# 单个 key 的精确体积:
redis-cli MEMORY USAGE user:feed:888
# (integer) 52431012
看到 50MB 级的 String,基本就是卡顿元凶了。
三、定位后怎么处理:拆分、异步删、分页读
删法和读法选错,反而会把坑挖更大。先看这张对比:
| 操作 | 命令 | 阻塞风险 | 适用场景 |
|---|---|---|---|
| 直接删除 | DEL big:key |
单线程同步删除,50MB 可能阻塞数秒,期间全实例卡死 | 仅测试环境 |
| 异步删除 | UNLINK big:key |
后台线程回收,几乎不阻塞主线程 | 生产首选 |
处理三板斧:
- 拆分 String:把 50MB 的 JSON 按业务分片成 user:feed:888:part1 … partN,每片控制在 100KB 以内。
- 分页读集合:永远别 LRANGE list 0 -1;改成 LRANGE 0 999、LRANGE 1000 1999;Hash 用 HSCAN 游标遍历。
- 限长:LPUSH 之后顺手 LTRIM my:list 0 9999,只保留最近一万条,从根上不让它膨胀。
踩坑实录
去年迁移一批老会话缓存,一个 String 硬塞了 52MB 用户 feed。我图省事直接 DEL 清掉,结果主线程卡了约 4 秒,监控瞬间一片 502。后来换成 UNLINK,并给新写入统一加 LTRIM 限长 10000,卡顿当场消失。教训很直白:线上删大 key,DEL 是雷,UNLINK 才是正路;预防比救火省事得多。
四、把大 key 挡在源头
与其天天救火,不如把扫描变成巡检:
- 写前评估:String 单值小于 1MB、集合元素小于 1 万;超标就分片或压缩。
- 给易膨胀的集合加 TTL,避免永久堆积。
- 月度巡检:低峰跑一次 –bigkeys -i 0.1,把 Top 10 记进监控看板。
常见问题
Q:–bigkeys 会锁库吗?
A:不会锁全库,但会抬升 ops。务必带 -i 0.1 限速,并在业务低峰执行;Redis 7 用 –memkeys 更平滑。
Q:MEMORY USAGE 显示 50MB,但 GET 一下客户端就超时?
A:这就是大 key 的典型症状——单次把 50MB 从服务端推到网络,带宽和客户端解析都被占满。改成分片存储、按需取分片即可。
Q:集群环境下大 key 还有额外坏处吗?
A:有。集群做 reshard / 迁移时,大 key 所在 slot 迁移会卡顿甚至超时失败,所以分片不仅要做,slot 也要尽量打散。
如果你也在 Redis 上踩过延迟的坑,欢迎到 fenij.com 留言说说你的场景,一起把这套排障经验补全。