redis大Key怎么删除
这个不是一个del就可以,而是问你懂不懂redis的单线程模型,懂不懂线上事故的规避,你以为del是O(1)的操作,del是O(N)的操作,N是元素个数,要是线上直接DEL一个500万字段的Hash,结果就是Redis主线程直接阻塞3~5秒,那设置一个过期时间,让它自己去删除?不要以为设置一个expire就完事大吉,过期删除,它也是同步的,Redis的主动过期扫描,如果扫到这个大BigKey,一样会卡主线程。
(1)分批删除,核心思路就是,化大为小,把一次O(N),换成N次的O(1),Hash用hscan每次取100个field来逐步删除,Set用sscan加srem,Zset用zscan加zrem,list用Itrim分批截断,每批之间sleep几十毫秒,在Redis 4.0以上引入了Unlink命令,和del语义完全一样,但删除操作,是交给后台lazyFree线程池,主线程立刻返回不阻塞,适合Key数量少,但是单Key特别大的场景,一般不会单独用unlink,因为lazy free线程池,默认只有四个线程,如果同时删除多个BigKey,一样会打满线程池,最安全的方式是组合使用,先分批缩小BigKey的体积,最后用unlink删除剩余的部分。
怎么提前发现bigkey,可以用命令Redis-cli--bigkeys参数全库扫描,或者用命令memory usage这个命令,查单个key的内存占用,生产环境一定要定期扫描,超过阈值就要告警;
AI回答:
我帮你把这段整理成“大厂面试可讲、技术准确、能被追问”的版本。先记住核心一句话:
Redis BigKey 删除不是
DEL一下那么简单。核心是:Redis 命令执行仍是单线程,DEL复杂类型是 O(N),会阻塞主线程;EXPIRE也不一定安全,主动过期抽到 BigKey 仍可能同步删除阻塞。正确做法是:提前发现、分批删除、UNLINK 异步、组合使用、源头预防。
一、先纠偏:你原答案里的关键点
-
DEL对复杂类型是 O(N)
Hash、Set、ZSet、List 删除时,要释放所有元素,N 是元素个数。500 万 field 的 Hash,直接DEL可能阻塞主线程数秒。 -
EXPIRE不等于安全
过期删除有被动和主动。主动过期扫描如果抽到 BigKey,默认同步删除,一样阻塞。Redis 4.0+ 可开启lazyfree-lazy-expire yes,让过期删除走异步。 -
UNLINK是异步删除,但线程池有限
UNLINK语义同DEL,但释放操作交给后台 lazy free 线程池,主线程立即返回。但后台线程资源有限,多个 BigKey 同时UNLINK可能排队,内存释放延迟。 -
最稳的是组合拳
先分批缩小 BigKey 体积,最后UNLINK删除剩余部分。不要单独依赖DEL,也不要盲目全量UNLINK。 -
发现 BigKey 要低峰、从库、采样、告警
redis-cli --bigkeys、MEMORY USAGE、DEBUG OBJECT、SCAN 采样、监控告警。生产环境避免高峰期全库扫描。
二、2 分钟面试逐字稿
这个问题表面是删除一个 key,实际考的是 Redis 单线程模型和线上事故规避。
首先,
DEL不是所有场景都 O(1)。对 String 可能很快,但对 Hash、Set、ZSet、List 这类复杂类型,DEL是 O(N),N 是元素个数。比如一个 500 万 field 的 Hash,直接DEL会释放大量内存,Redis 主线程被阻塞,其他命令全部排队,可能引发超时甚至雪崩。那设置过期时间让 Redis 自己删行不行?也不一定。Redis 过期删除有被动和主动。主动过期扫描如果抽到这个 BigKey,默认同步删除,一样会卡主线程。除非开启
lazyfree-lazy-expire yes,让过期删除走异步。所以正确做法是分批删除,化大为小。Hash 用
HSCAN每次取 100 个 field,再HDEL;Set 用SSCAN+SREM;ZSet 用ZSCAN+ZREM;List 用LTRIM分批截断。每批之间 sleep 几十毫秒,让主线程有机会处理其他命令。Redis 4.0 以上可以用
UNLINK,语义和DEL一样,但删除操作交给后台 lazy free 线程池,主线程立即返回,适合 key 数量少、单 key 特别大的场景。但一般不单独用UNLINK,因为后台线程资源有限,默认线程数不多,同时删多个 BigKey 可能打满线程池。最安全的是组合使用:先分批缩小 BigKey 体积,最后用UNLINK删除剩余部分。怎么提前发现 BigKey?可以用
redis-cli --bigkeys全库扫描,或者MEMORY USAGE key查单个 key 内存占用。生产环境要定期扫描,超过阈值告警。但扫描建议在低峰期、从库执行,避免影响主库。最后预防:设计阶段拆分 BigKey,控制 value 大小和元素数量;开启 lazy free 相关配置;删除用
UNLINK;设置过期时间也要考虑 lazy free;定期巡检和告警。
三、关键点拆解
1. 为什么 DEL 会阻塞?
Redis 6.0 虽然网络 IO 多线程,但命令执行仍是单线程。
DEL key:
- String:释放 value,通常较快,但 value 巨大也会慢;
- Hash/Set/ZSet/List:需要遍历并释放所有元素,O(N);
- N 越大,阻塞越久。
500 万 field 的 Hash,释放内存可能几秒,期间所有请求排队。
2. 为什么 EXPIRE 也不安全?
Redis 过期删除:
- 被动删除:访问 key 时发现过期,同步删除;
- 主动删除:定期随机抽样过期 key,删除。
如果抽到 BigKey,默认同步删除,仍会阻塞主线程。
开启 lazyfree-lazy-expire yes 后,过期删除可走异步。
相关配置:
lazyfree-lazy-expire yes
lazyfree-lazy-eviction yes
lazyfree-lazy-server-del yes
lazyfree-lazy-user-del yes
replica-lazy-flush yes
3. 分批删除方案
| 类型 | 分批扫描 | 分批删除 |
|---|---|---|
| Hash | HSCAN key cursor COUNT 100 |
HDEL key field1 ... field100 |
| Set | SSCAN key cursor COUNT 100 |
SREM key member1 ... member100 |
| ZSet | ZSCAN key cursor COUNT 100 |
ZREM key member1 ... member100 |
| List | 无 SCAN | LTRIM key 1000 -1 分批删除头部 1000 个 |
要点:
- 每批 100 左右,按业务调整;
- 每批之间 sleep 几十毫秒;
- 客户端循环执行,不要用 Lua sleep,Lua 会阻塞;
- 删除到剩余较少时,用
UNLINK收尾。
4. UNLINK 怎么用?
UNLINK key:
- Redis 4.0+;
- 语义同
DEL; - 主线程把 key 从 keyspace 摘除,立即返回;
- 实际内存释放由后台 lazy free 线程池完成。
适合:
- key 数量少;
- 单 key 特别大;
- 能接受内存延迟释放。
不适合:
- 大量 BigKey 同时删;
- 后台线程池打满时,释放排队,内存峰值升高。
所以最稳:
先
HSCAN/HDEL等分批缩小,最后UNLINK删除剩余 key。
5. 怎么发现 BigKey?
常用方式:
redis-cli --bigkeys:全库 SCAN,统计每种类型最大 key;MEMORY USAGE key:查单个 key 内存占用;DEBUG OBJECT key:看 serializedlength,生产慎用;SCAN+MEMORY USAGE采样;- 监控:内存突增、慢查询、延迟、latency monitor。
注意:
- 低峰期执行;
- 优先从库;
- 采样即可,避免全量扫描拖垮线上;
- 设置阈值告警,比如 String > 10KB,集合元素 > 1000,具体按业务定。
6. 源头预防
- 设计阶段避免 BigKey:拆分 Hash、List、Set;
- 控制 value 大小,压缩、序列化优化;
- 删除用
UNLINK替代DEL; - 开启 lazy free 相关配置;
- 过期时间结合
lazyfree-lazy-expire; - 定期 BigKey 巡检和告警;
- 低峰执行删除,避免高峰期批量操作。
四、面试官可能追问
Q1:DEL 和 UNLINK 区别?
DEL同步删除,复杂类型 O(N),阻塞主线程;UNLINK异步删除,主线程摘除 key 后立即返回,内存释放交给后台线程。
Q2:为什么 EXPIRE 也会阻塞?
主动过期扫描如果抽到 BigKey,默认同步删除,仍会 O(N) 释放内存。开启
lazyfree-lazy-expire yes后可异步。
Q3:lazy free 线程池打满怎么办?
多个 BigKey 同时
UNLINK,后台线程排队,内存释放延迟,可能内存峰值。所以先分批缩小,再UNLINK收尾,并控制并发删除数量。
Q4:分批删除会遗漏吗?
用 SCAN 系列游标遍历,删除已返回元素没问题。迭代中数据集变化可能返回重复,但不会导致已删元素复活。处理重复即可。
Q5:生产环境怎么安全执行?
低峰期、从库扫描、客户端分批、sleep、监控延迟、限流、灰度、准备好回滚和告警。
Q6:Redis 6 多线程能解决吗?
Redis 6 多线程主要用于网络 IO,命令执行仍是单线程。BigKey 删除仍可能阻塞主线程。
Q7:List 怎么分批删?
用
LTRIM key 1000 -1每次删头部 1000 个,循环到剩余较少,再UNLINK。不要一次LTRIM删除大量元素。
Q8:怎么预防 BigKey?
拆分、控制大小、UNLINK、lazy free、过期策略、巡检告警、低峰删除。
五、收尾话术
所以 BigKey 删除的核心不是
DEL还是UNLINK,而是理解 Redis 单线程模型和 O(N) 阻塞风险。生产上要提前发现、分批删除、异步收尾、低峰执行、监控告警,并从设计上避免 BigKey。这样既能删掉数据,又不会把 Redis 主线程打挂。

浙公网安备 33010602011771号