DEL 同步删除大 Key 会阻塞主线程导致 Redis “假死”,UNLINK 则先解引用再由 BIO 线程异步释放内存,避免延迟飙升,是生产环境硬性规范。

必须用 UNLINK 替代 DEL,否则大 Key 删除必然卡主线程。这不是优化建议,而是生产环境的硬性操作规范。
为什么 DEL 会让 Redis 瞬间“假死”
Redis 主线程执行 DEL 时,不是删个指针就完事——它得把整个数据结构从内存里彻底清空。比如一个含 200 万个成员的 ZSet,主线程必须逐个遍历节点、调用 free()、归还内存块。这个过程无法中断,期间所有命令排队等待,P99 延迟直接飙升到几百毫秒甚至秒级。
典型现象包括:INFO commandstats 中 del 的 usec_per_call 突增、监控看到 latency spikes、SLOWLOG 里出现耗时超标的 DEL 记录。
- String 超过 10MB、Hash/Set/ZSet 成员数 > 10 万、List 长度 > 50 万,基本可判定为高风险大 Key
-
DEL时间复杂度不是 O(1),而是 O(N),N 是实际元素数或字节数 - 即使开了
io-threads,对DEL也完全无效——多线程只处理网络读写,不碰内存释放
UNLINK 是怎么做到“删得快还不卡”的
UNLINK 把删除拆成两步:第一步是主线程瞬间解引用(O(1)),第二步是后台 BIO 线程异步回收内存。客户端收到响应后,Key 已不可见;而内存释放这件事,交给后台慢慢干。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
关键点在于:UNLINK 返回极快(通常 used_memory 不会立刻下降——你要看 lazyfree_pending_objects 是否先升后降,这才是真正异步生效的证据。
- 必须确保
CONFIG GET lazyfree-lazy-user-del返回["lazyfree-lazy-user-del","yes"],否则UNLINK会退化为同步行为 - 对不存在的 Key,
UNLINK返回 0,语义和DEL一致,业务代码无需改逻辑 - 不支持在 Lua 脚本中调用
redis.call("UNLINK", ...),会报错;需拆成外部调用
哪些情况 UNLINK 会悄悄变回 DEL
它不是无条件异步。遇到以下任一情形,UNLINK 会自动同步执行,且不提示、不报错——你得自己意识到它失效了。
- Key 正被
WATCH监控(事务上下文未提交) -
OBJECT REFCOUNT查出 refcount > 1(比如刚被RENAME引用过) - 后台 BIO 线程积压严重,
INFO bio显示bio_num_bgrewrite或bio_num_aof_rewrite长期满载 - 内存极度紧张时,
mem_not_counted_for_lazyfree上升,后台线程主动拒收新任务
上线前必须验证的三件事
别只信返回快,要确认后台真在干活。最简验证路径:
- 执行
UNLINK big:key前后,快速跑两次INFO memory,观察lazyfree_pending_objects是否从 0 → 正数 → 回落 - 对比
MEMORY USAGE big:key和used_memory_human变化节奏:后者缓慢下降才说明异步生效 - 检查客户端版本:老版 Jedis(UNLINK,得用
execute_command("UNLINK", key)
Redis 4.0+ 是硬门槛,低于此版本会报 ERR unknown command `unlink`;线上混用不同版本时,这个错误容易被日志淹没,务必提前扫一遍。

















