Memcached 过期与删除机制:Lazy Expiration 与 LRU 策略详解
Memcached 作为内存缓存系统,其过期数据处理和内存回收机制直接影响缓存有效性和资源利用率。与传统数据库的主动过期检查不同,Memcached 采用懒惰删除(Lazy Expiration) 和LRU(最近最少使用) 策略,以最小的性能开销实现内存管理。本文深入解析这两种机制的原理、优缺点及实际表现。
Lazy Expiration(延迟过期):不主动检查,按需验证
核心原理
Memcached 不会主动监控数据是否过期,而是在用户尝试获取数据时才检查其过期时间:
- 当客户端执行
get <key>时,Memcached 首先判断该键是否存在。 - 若存在,检查其时间戳是否过期(当前时间 > 过期时间)。
- 若已过期,返回 “键不存在”(
END),并将该键标记为无效,从curr_items统计中移除。 - 若未过期,返回数据,并更新其 “最近使用时间”(用于 LRU 策略)。
优点
- 节省 CPU 资源:无需维护定时器或线程监控所有键的过期状态,避免频繁检查带来的性能损耗。
- 简单高效:过期检查与数据访问绑定,仅在必要时执行,适合高并发场景。
缺点
- 内存占用延迟释放:过期数据可能长时间驻留内存(直到被访问或被 LRU 淘汰),导致短期内存利用率下降。
- 统计数据延迟更新:
stats命令中的curr_items会包含已过期但未被访问的键,直至其被主动获取或淘汰。
示例
存储一个 10 秒后过期的键:
1
2set test_key 0 10 5
hello5 秒后执行
stats,curr_items仍包含test_key(未被访问,未触发检查)。15 秒后执行
get test_key,返回END(已过期),此时curr_items减 1。
LRU(最近最少使用):内存不足时的回收策略
当 Memcached 内存空间不足(达到 -m 参数限制),且没有过期数据可复用(或过期数据空间不足)时,会通过 LRU 策略 淘汰 “最近最少使用” 的数据,释放内存给新数据。
核心原理
- 维护访问记录:每个键被访问(
get/set等操作)时,Memcached 会更新其 “最近使用时间戳”。 - 淘汰规则:当需要分配新内存时,优先淘汰时间戳最早(最久未被使用)的键,无论其是否过期(包括永久有效的键)。
关键特性
- 不区分过期与永久数据:即使键设置为永久有效(
expire=0),若长期未被访问,仍可能被 LRU 淘汰。 - 按 Slab Class 独立淘汰:LRU 操作仅在当前需要分配的 Slab Class(对应固定大小的 Chunk)内部进行,不会跨类别淘汰数据。
示例
- 假设 Memcached 总内存为 1MB,且仅使用 Chunk 大小为 100B 的 Slab Class。
- 存储 10000 个 100B 的永久有效键(填满内存)。
- 新增第 10001 个键时,Memcached 会淘汰这 10000 个键中最久未被访问的那个,腾出空间。
懒惰删除机制:内存复用的细节
Memcached 在删除数据(delete 命令)或处理过期数据时,采用标记删除而非立即释放内存:
- 删除操作:执行
delete <key>后,键被标记为 “已删除”,但其占用的 Chunk 不会立即归还给操作系统,而是加入该 Slab Class 的空闲列表。 - 内存复用:新数据需要分配 Chunk 时,优先使用空闲列表中的标记删除或过期 Chunk,避免频繁向操作系统申请内存。
优点
- 减少系统调用:内存块复用避免了
free和malloc的开销,提升分配效率。 - 维持 Slab 结构稳定:Chunk 大小固定,复用机制保证了 Slab Class 结构不被破坏,便于管理