unlink不是del的升级版,而是专为大key设计的异步替代方案;它需redis≥4.0版本且lazyfree-lazy-user-del配置为yes才生效,否则静默退化为同步del,string类型始终同步释放,小key(如元素≤64)也不异步,批量删须scan+unlink,lua中不可调用。

UNLINK不是DEL的“升级版”,而是专为大Key设计的异步替代方案;直接全局替换DEL为UNLINK,大概率踩坑——版本不支持、配置未开、Lua里调用失败、小Key白换、内存水位不降等问题会立刻暴露。
UNLINK生效的前提条件必须手动确认
UNLINK命令本身不保证异步,它依赖两个硬性前提:
-
CONFIG GET lazyfree-lazy-user-del必须返回["lazyfree-lazy-user-del","yes"],否则 UNLINK 会静默退化为同步 DEL(无报错、无提示) - Redis 版本 ≥ 4.0 —— 低于此版本执行
UNLINK直接报错ERR unknown command `unlink`
线上切之前务必在目标实例上跑这两条命令验证。别信文档说“支持”,要实测。老版本 Redis(如 3.2)即使客户端封装了 UNLINK,实际发过去也是失败。
UNLINK对不同数据结构的行为差异极大
它不是“一律异步”,而是按数据结构和规模智能决策:
- String 类型:无论多大(哪怕 100MB),
UNLINK都同步释放——因为 SDS 内存释放是 O(1),异步调度反而增加开销 - List/Hash/Set/ZSet:元素数 ≤ 64(默认阈值,由
LAZYFREE_THRESHOLD宏控制)时,仍走主线程同步释放 - 真正进后台 BIO 线程异步队列的,只有那些“大而复杂”的 Key,比如含 50 万成员的 ZSet 或嵌套深的 Hash
所以别看到 Key 名字长就以为它是大Key——得用 MEMORY USAGE key 和 TYPE key + STRLEN/HLEN/ZCARD 组合判断。删之前不查,等于盲删。
批量删和模糊删必须绕过 UNLINK 直接用 SCAN + UNLINK
UNLINK 本身不支持通配符或模式匹配,不能写 UNLINK "user:*"。生产中所有“按前缀删”都必须走管道组合:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
redis-cli --scan --pattern "log:2025*" -n 2 | xargs -r redis-cli -n 2 unlink
注意三点:
- 必须加
-r(xargs 的选项),否则SCAN返回空时会执行裸redis-cli unlink,报错中断 - SCAN 默认游标只遍历 10 条,高并发删建议加
--count 1000减少轮次 - 绝对禁用
KEYS命令——它会阻塞主线程,哪怕只是临时在从库上试,也属于高危操作
脚本里写死 KEYS 的,上线前必须重写。
Lua 脚本里不能调用 UNLINK,但可以迂回处理
redis.call("UNLINK", key) 在任何 Redis 版本都会报错 ERR unknown command —— Lua 环境根本不识别 UNLINK。常见错误是想在原子逻辑里删大Key,结果脚本崩掉。
可行解法只有两种:
- 拆成两步:Lua 脚本里只做
EXISTS或DEL(小Key场景),大Key删动作移出脚本,由外部程序判断后调用UNLINK - 用
DEL+CONFIG SET lazyfree-lazy-user-del yes(如果已开启),让 DEL 自动触发异步释放——但注意这会影响所有 DEL,且需确认业务能接受该配置全局生效
别试图 patch 客户端或魔改 Lua,成本远高于重构调用链。
最常被忽略的一点:UNLINK 后 INFO memory 的 used_memory 不会立刻下降,要看 lazyfree_pending_objects 是否先升后降——这才是异步真正起效的证据。盯着 used_memory 等回落,可能等一分钟都不动,不是没删,是还没回收完。










