UNLINK可替代DEL异步删除大Key,避免主线程阻塞;但需开启lazyfree-lazy-user-del配置,且在WATCH监控、refcount>1等场景仍同步执行。

Redis 在内存接近 maxmemory 时,用 DEL 同步删大 key 极易引发主线程卡顿、请求超时甚至连接堆积——这不是配置问题,是设计使然。真正该用的是 UNLINK,但它不是“无脑替换”,得理解它何时异步、何时退化、以及为什么不能完全替代 DEL。
UNLINK 不等于“立刻释放内存”
UNLINK 的核心动作只有两步:主线程立即从数据库字典中摘掉 key(O(1)),然后把实际内存回收任务扔进后台异步队列。这意味着:
- 客户端再 GET 这个 key 立即返回
(nil),行为上已“删除” - 但内存占用不会马上下降,
INFO memory中的used_memory可能几秒甚至更久才回落 - 如果后台线程积压或阻塞(比如同时大量
UNLINK+FLUSHDB ASYNC),内存释放会进一步延迟
所以别指望 UNLINK 能“秒降内存水位”——它解决的是响应延迟,不是内存压力本身。
UNLINK 什么时候会退化成同步删除
Redis 并非对所有 key 都走异步。它内部有成本判断逻辑(基于 LAZYFREE_THRESHOLD 宏,默认 64):
- 对
list/hash/set/zset:元素个数 ≤ 64 → 直接主线程同步释放,不进队列 - 对
string:无论多长,一律同步释放(因为sds内存释放是 O(1),异步反而增加调度开销) - 对大 key(如一个含 10 万成员的
zset):才会真正进异步队列,由后台线程处理
也就是说,UNLINK 是智能的,但它的“智能”依赖于数据结构类型和规模——你得知道你删的 key 属于哪一类,否则可能误判效果。
内存快见顶时,UNLINK 也救不了根本问题
当 used_memory 已逼近 maxmemory,哪怕全用 UNLINK,风险依然很高:
- 后台线程释放内存的速度跟不上新写入或新 key 创建的速度,OOM 仍可能发生
- 如果此时触发了内存淘汰(如
allkeys-lru),淘汰过程本身也会争抢 CPU 和锁资源 -
UNLINK不影响淘汰策略决策——Redis 仍按旧的内存占用值做驱逐判断,可能误删不该删的 key
真正要做的,是在监控层面盯住 mem_fragmentation_ratio 和 lazyfree_pending_objects,前者过高说明内存碎片严重,后者持续 > 0 表明异步队列已在积压——这时就得人工介入,而不是继续发 UNLINK。
生产环境删大 key 的实操建议
别只盯着命令换不换,重点在节奏和兜底:
- 先用
MEMORY USAGE key确认 key 真的是“大”(比如 > 1MB),再决定是否UNLINK - 批量删多个大 key 时,用管道(pipeline)包住多个
UNLINK,但单次不超过 100 个,避免压爆异步队列 - 删完立刻查
INFO MEMORY,关注lazyfree_pending_objects是否归零;不归零就暂停后续清理操作 - 对必须立刻释放内存的场景(如紧急腾出空间),只能接受
DEL的短暂卡顿,或提前拆分 key(如用 hash 分片代替单个大 set)
UNLINK 是个好工具,但它的价值不在“删得快”,而在“不拖垮服务”。用错时机,它只是把问题从前台挪到了后台队列里而已。


















