lazyfree-lazy-eviction关闭时,Redis淘汰大Key会同步阻塞主线程:因淘汰路径绕过UNLINK命令,直接调用内部释放函数,且不启用bio后台线程,导致O(N)级遍历释放(N为元素数≥64),引发毫秒至秒级延迟。

lazyfree-lazy-eviction 关闭时,淘汰大Key必然同步释放内存
Redis 6.0 默认淘汰策略(如 allkeys-lru)在触发驱逐时,会先选中 key,再立即执行释放逻辑。一旦选中的是一个 hash 含 200 万 field 的大 Key,del 级别的释放就会全程跑在主线程——这不是“慢一点”,而是毫秒级阻塞直接拉长到数百毫秒甚至秒级。此时所有新请求排队,P99 延迟飙升,监控看到 evicted_keys 突增但 used_memory_human 下降滞后,就是典型症状。
为什么 lazyfree-lazy-eviction= no 就等于放弃后台线程
该配置控制的是“淘汰路径”是否启用异步释放,它和 UNLINK 或 DEL 命令无关,只作用于 maxmemory 触发的自动驱逐流程。关闭后:
- 即使你业务里全用
UNLINK,也救不了淘汰场景——因为淘汰不走命令路径,它绕过客户端直接调用内部释放函数 - 后台 bio 线程完全不参与,
INFO stats中的lazyfree_pending_objects始终为 0 -
CONFIG SET lazyfree-lazy-eviction yes是运行时唯一生效方式,写进redis.conf才能持久化
别被“大Value”误导:真正卡住的是元素数量,不是字节数
一个 50KB 的 hash,如果只有 10 个 field,删除很快;但一个 8MB 的 hash,含 150 万 field,哪怕每个 field 只有几十字节,释放时仍要遍历全部 dict entry、逐个释放 value 对象、回收内存页——这才是 O(N) 阻塞根源。Redis 判断是否值得异步释放,依据是结构内元素个数 ≥ LAZYFREE_THRESHOLD(硬编码为 64),不是 MEMORY USAGE 返回的字节数。
开了 lazyfree-lazy-eviction 也不代表万事大吉
异步释放只是把内存回收甩给后台线程,但后台处理能力有上限:
- 若淘汰压力持续高于 bio 线程吞吐(比如每秒淘汰上百个大 Key),
lazyfree_pending_objects会持续上涨,内存实际归还不及时 - 后台线程释放内存后,glibc malloc 不一定立刻把页还给 OS,
used_memory_rss可能长期高于used_memory - 主从复制场景下,异步释放不影响传播:从库仍需同步执行相同释放动作,所以从库一样可能卡
真正难处理的,是淘汰速率和后台回收速率之间的持续失衡——这时光调配置没用,得靠拆实例、升配或提前清理大 Key。











