根本原因是redis主从模式下过期键清理仅由主节点通过惰性删除和定期删除触发,从节点不执行任何过期判断或删除逻辑,且两种机制均存在延迟:惰性删除依赖访问触发,定期删除受hz参数和负载限制,抽样检查无法保证及时覆盖所有过期键。

Redis主从模式下过期键不立即释放内存,根本原因在于:从节点(slave)**不执行任何过期判断或删除逻辑**,所有过期键的清理动作只发生在主节点(master),且仅通过被动删除和定期删除两种机制触发——而这两者都存在延迟。
从节点完全不处理过期键
从节点只负责接收主节点的写命令(包括 DEL、EXPIRE 等)并重放,它自身不会调用 expireIfNeeded(),也不会运行 activeExpireCycle()。这意味着:
- 即使一个 key 在从节点上已过期,只要没被主节点显式删除并同步过来,它就一直存在于从节点内存中;
- 从节点的
TTL命令返回值,是根据主节点同步来的过期时间戳与本地时间对比计算的,但这个计算不触发删除; - 主节点上因惰性删除或定期删除产生的
DEL命令,会作为普通写命令传播到从节点,这才是从节点真正“删掉”该 key 的唯一时机。
主节点的被动删除在主从场景中更不可靠
被动删除依赖客户端访问触发 expireIfNeeded(),但在主从分离部署中,读请求常被路由到从节点,而从节点不执行该函数——所以一次 GET 请求打到从节点,既不会删 key,也不会通知主节点去删。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 如果业务读写分离严格,某个过期 key 长期只被从节点读取,那它在主节点上也可能长期滞留(除非被定期删除扫到);
- 主节点上未被访问的过期 key,只能等
activeExpireCycle()抽样检查时被发现并删除,再通过DEL命令同步给从节点; - 若该 key 恰好没被抽中,又没被任何客户端访问,就会形成“双端僵尸”:主从都存着,但谁都懒得删。
定期删除的频率受 hz 和负载双重限制
hz 参数控制 serverCron 每秒执行次数,默认为 10,即每 100ms 检查一次过期键。但每次检查并非全量扫描:
- 每次只随机抽取 20 个设置了过期时间的 key 进行检查;
- 若其中超过 25% 已过期,则立即再抽一轮,最多连续执行 25 毫秒(硬上限);
- 当 Redis 负载高、主线程忙于处理大量请求时,
activeExpireCycle()可能被跳过或大幅缩减执行时间; - 也就是说,即使主节点上堆了上万过期 key,也可能需要数分钟甚至更久才能被全部清理完。
真正容易被忽略的是:过期键的内存释放不是原子事件,它依赖主节点的主动清扫节奏 + 命令同步链路 + 从节点的执行确认。任意一环卡住(比如网络延迟导致 DEL 未及时到达、从节点积压重放、hz 配得太低),都会让过期 key 在内存里多躺一阵子——这不是 bug,是设计权衡的结果。










