unlink 可直接删除超长 list,但需 redis ≥ 4.0 且客户端真正发送 unlink 命令;其先 o(1) 解引用,元素数 > 64 时由 bio 异步回收 quicklist;卡顿主因是 bio 积压、编码非 quicklist 或客户端未真正调用 unlink。

UNLINK 能不能直接删超长 List
能,但必须确认 Redis 服务端版本 ≥ 4.0(6.x 完全支持),且客户端实际发出了 UNLINK 命令而非降级为 DEL。UNLINK 对 List 的处理逻辑是:先从键空间立即解引用(O(1)),再根据内部评估决定是否异步回收——只要该 List 的元素个数 > 64(LAZYFREE_THRESHOLD 默认值),就会交由 BIO 线程异步遍历 quicklist 节点并逐个 free()。
为什么用 UNLINK 删百万级 List 还会卡一下
不是 UNLINK 失效,而是你踩中了几个隐性条件:
-
UNLINK返回快 ≠ 内存立刻归还:used_memory指标不会马上下降,得等 BIO 线程完成回收,期间INFO memory里mem_not_counted_for_evict会上升 - BIO 队列已积压:如果
INFO bio显示bio_pending_job_count长期 > 0,说明后台线程忙不过来,新提交的释放任务要排队 - List 实际编码不是 quicklist:极小 List 可能用 ziplist 编码,此时 UNLINK 会退化为同步删除(因成本低),不触发异步路径
- 客户端没真正调用 UNLINK:老版 Jedis 或 redis-py 默认无
unlink()方法,需显式execute_command("UNLINK", key)
批量删多个超长 List 怎么避免压垮 BIO
UNLINK 本身不阻塞,但高频提交大对象释放请求会让 BIO 线程持续高负载,影响 AOF 重写、RDB 保存等后台任务。稳妥做法是控制节奏:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 单次
UNLINK不超过 10 个 key,避免一次性塞爆 BIO 队列 - 用
SCAN分批获取 key(别用KEYS),每批 SCAN 后加SLEEP 0.1(Redis 6.0+ 支持)再执行 UNLINK - 监控
INFO bio中bio_pending_job_count,若连续 > 50,暂停下发新 UNLINK - 紧急清理时,可临时调大
bio_num_background_jobs(需 CONFIG REWRITE + 重启才生效,慎用)
Lua 脚本里不能调 UNLINK 是硬伤
Redis 明确禁止在 Lua 中执行 redis.call("UNLINK", key),会报错 ERR unknown command `unlink`。这不是客户端问题,是服务端设计限制。真实业务中遇到“脚本判断后删大 List”,只能拆成两步:
- 先在 Lua 里用
EXISTS+LLEN判断 key 是否存在且长度超标 - 把需要删的 key 列表返回给应用层,由外部调用
UNLINK - 若必须原子性,改用
DEL——但得接受短时延迟毛刺,或提前对 List 做分片(如按时间切为list:20260501、list:20260502)
最易被忽略的一点:UNLINK 的“异步”只管内存回收,不保证 key 立即从所有副本消失。主从复制流里它仍是原子命令,但从库收到的是 UNLINK 操作本身,不是 DEL;如果从库 BIO 滞后,used_memory 下降会比主库慢几秒。










