Skip to content

Memcached 数据过期

数据过期机制

1. 数据过期的定义与作用

  • 数据过期:指 Memcached 中存储的数据在达到预设的时间后自动失效并被删除的机制
  • 过期时间:也称为 TTL(Time To Live),单位为秒
  • 作用
    • 自动清理不再需要的数据
    • 释放内存空间
    • 确保数据的时效性
    • 避免缓存数据与源数据不一致

2. 数据过期的类型

  • 绝对过期

    • 从数据存储时开始计算,经过固定时间后过期
    • 例如:设置 TTL 为 3600 秒,数据将在存储 1 小时后过期
    • 最常用的过期类型
  • 相对过期

    • 从数据最后一次访问开始计算,经过固定时间后过期
    • 例如:设置 TTL 为 3600 秒,数据在 1 小时内未被访问则过期
    • 适用于热点数据,可延长热点数据的生存时间

3. 数据过期的限制

  • 最大 TTL

    • Memcached 支持的最大 TTL 为 2^31-1 秒(约 68 年)
    • 当设置 TTL 为 0 时,表示数据永不过期
    • 当设置 TTL 大于 30 天(2592000 秒)时,Memcached 会将其视为 Unix 时间戳
  • 精度

    • 过期时间的精度为秒级
    • 实际过期时间可能会有轻微延迟
    • 过期检查是周期性的,不是实时的

4. 过期检查方式

  • 惰性删除

    • 当客户端访问数据时,检查数据是否过期
    • 若过期,则删除数据并返回未命中
    • 优点:节省 CPU 资源,只在必要时检查
    • 缺点:过期数据可能会占用内存较长时间
  • 主动删除

    • Memcached 定期主动检查并删除过期数据
    • 默认每 60 秒运行一次过期检查
    • 每次检查随机选择 100 个 item
    • 若其中超过 25% 的 item 过期,则继续检查
    • 优点:及时释放内存空间
    • 缺点:消耗一定的 CPU 资源

5. 过期删除流程

  1. 标记过期:当数据达到过期时间时,被标记为过期
  2. 删除检查:通过惰性删除或主动删除机制检查过期数据
  3. 内存回收:删除过期数据,回收内存空间
  4. 更新统计:更新 expired_unfetchedevicted_unfetched 等统计指标

6. 过期与驱逐的区别

特性过期(Expiration)驱逐(Eviction)
触发原因达到预设的 TTL内存不足
处理方式自动删除根据驱逐策略删除
优先级高(过期数据优先被删除)低(只有在内存不足时才发生)
统计指标expired_unfetchedevictions

数据过期配置

1. 设置过期时间

  • 命令行设置

    set key flags exptime bytes [noreply]
    value
    • exptime:过期时间(秒)
    • 示例:set mykey 0 3600 5\nvalue(设置过期时间为 1 小时)
  • 不同客户端设置方式

    • JavamemcachedClient.set("mykey", 3600, "value")
    • Pythonclient.set("mykey", "value", time=3600)
    • PHP$memcached->set("mykey", "value", 3600)
    • Node.jsclient.set("mykey", "value", {expiry: 3600}, callback)

2. 批量设置过期时间

  • touch 命令:用于更新现有键的过期时间,不改变键值

    touch key exptime [noreply]
    • 示例:touch mykey 7200(将过期时间延长至 2 小时)
  • 批量操作

    • 部分客户端支持批量设置过期时间
    • 可通过循环调用 touch 命令实现批量更新

3. 清除过期数据

  • flush_all 命令

    • 用于清除所有缓存数据
    • 可带过期时间参数,表示延迟清除
    • 示例:flush_all 3600(1 小时后清除所有数据)
    • 注意:该命令会清除所有数据,使用需谨慎
  • 清除特定前缀的键

    • Memcached 不直接支持按前缀清除
    • 需通过遍历所有键或使用命名空间实现
    • 推荐使用命名空间管理相关键

过期策略优化

1. 合理设置 TTL

  • 根据数据特性设置

    • 频繁变化的数据:TTL 较短(分钟级)
    • 稳定数据:TTL 较长(小时或天级)
    • 静态数据:可设置较长 TTL 或永不过期
  • 避免 TTL 过于集中

    • 设置随机过期时间,避免缓存雪崩
    • 示例:TTL = base_ttl + random(0, variation)
    • 推荐 variation 为 base_ttl 的 10%-20%
  • 考虑业务需求

    • 兼顾数据时效性和缓存命中率
    • 避免频繁更新导致的性能开销

2. 命名空间管理

  • 命名空间的作用

    • 方便管理相关的键
    • 支持批量失效
    • 避免键冲突
  • 实现方式

    • 在键名前添加前缀:{namespace}:{key}
    • 通过更新命名空间版本实现批量失效
    • 示例:
      python
      # 获取命名空间版本
      ns_version = client.get("user_namespace") or 1
      
      # 构造带命名空间的键
      key = f"user:{ns_version}:{user_id}"
      
      # 批量失效:更新命名空间版本
      client.incr("user_namespace")

3. 热点数据处理

  • 热点数据识别

    • 通过监控工具识别热点数据
    • 分析访问日志,统计访问频率
    • 使用 Memcached 的 stats detail 命令
  • 热点数据优化

    • 延长热点数据的 TTL
    • 考虑使用本地缓存
    • 避免热点数据集中过期
    • 实现多级缓存架构

4. 缓存雪崩预防

  • 缓存雪崩

    • 定义:大量缓存数据同时过期,导致大量请求回源
    • 影响:后端数据库负载激增,可能导致系统崩溃
  • 预防措施

    • 设置随机过期时间
    • 采用分层缓存架构
    • 实现缓存预热
    • 配置合理的重试和降级机制
    • 监控缓存命中率,及时发现异常

过期数据监控

1. 监控指标

  • expired_unfetched:过期且未被获取的 item 数量
  • evicted_unfetched:因内存不足而驱逐且未被获取的 item 数量
  • curr_items:当前存储的 item 数量
  • total_items:自启动以来存储的总 item 数量
  • evictions:因内存不足而驱逐的 item 数量

2. 监控方法

  • 命令行监控

    bash
    echo stats | nc localhost 11211 | grep -E "expired_unfetched|evicted_unfetched|curr_items|total_items|evictions"
  • 监控系统

    • Prometheus + Grafana:通过 exporter 采集过期相关指标
    • Zabbix:使用内置的 Memcached 模板
    • Datadog:自动采集 Memcached 指标

3. 告警设置

  • 告警阈值

    • expired_unfetched 增长率异常升高
    • evictions 持续增加
    • curr_items 突然下降
    • 命中率突然下降
  • 告警渠道

    • 即时通讯工具(企业微信、钉钉等)
    • 短信/电话(紧急情况)
    • 邮件(非紧急情况)

常见过期问题及解决方案

1. 数据提前过期

  • 症状:数据在设置的 TTL 之前就被删除

  • 可能原因

    • 内存不足导致数据被驱逐
    • 系统时间变化
    • 客户端与服务器时间不一致
    • Memcached 版本 bug
  • 解决方案

    • 增加内存容量
    • 检查系统时间和时区设置
    • 确保客户端与服务器时间同步
    • 升级到稳定版本

2. 数据过期后仍可访问

  • 症状:数据超过 TTL 后仍能被获取

  • 可能原因

    • 惰性删除机制,数据未被访问
    • 主动删除机制尚未运行
    • TTL 设置为 0(永不过期)
  • 解决方案

    • 手动访问数据触发惰性删除
    • 等待主动删除机制运行(默认 60 秒)
    • 检查 TTL 设置是否正确
    • 使用 flush_all 命令强制清除

3. 缓存雪崩

  • 症状:大量请求回源,后端数据库负载激增

  • 可能原因

    • 大量数据同时过期
    • 缓存服务不可用
    • 热点数据集中过期
  • 解决方案

    • 设置随机过期时间
    • 采用分层缓存架构
    • 实现缓存预热
    • 配置合理的重试和降级机制
    • 监控缓存命中率,及时发现异常

4. 缓存穿透

  • 症状:大量请求访问不存在的数据,导致回源

  • 可能原因

    • 恶意攻击
    • 业务逻辑问题
    • 数据已删除但请求仍在进行
  • 解决方案

    • 实现布隆过滤器
    • 缓存空值
    • 限制请求频率
    • 验证请求参数

过期策略最佳实践

1. TTL 设计原则

  • 分层设置

    • 核心数据:较短 TTL(分钟级)
    • 热点数据:较长 TTL(小时级)
    • 静态数据:长期 TTL(天级或更长)
  • 随机化 TTL

    • 在基础 TTL 上添加随机值
    • 避免缓存雪崩
    • 推荐随机范围为基础 TTL 的 10%-20%
  • 动态调整

    • 根据业务需求和数据变化频率调整 TTL
    • 监控命中率,优化 TTL 设置
    • 实现自适应 TTL 机制

2. 命名空间管理

  • 使用命名空间

    • 按业务模块或数据类型划分命名空间
    • 方便管理和批量失效
    • 避免键冲突
  • 命名空间版本控制

    • 使用版本号管理命名空间
    • 通过更新版本号实现批量失效
    • 示例:{namespace}:{version}:{key}

3. 监控与优化

  • 建立监控体系

    • 监控过期相关指标
    • 设置合理的告警阈值
    • 定期分析过期数据分布
  • 优化过期检查

    • 调整主动删除的频率(通过 maxconns_fast 等参数)
    • 优化内存使用,减少驱逐
    • 合理设置 slab 分配,减少内存浪费

4. 结合业务场景

  • 电商场景

    • 商品信息:TTL 较短(30 分钟)
    • 促销信息:根据活动时间设置 TTL
    • 用户会话:TTL 较长(几小时)
  • 新闻资讯场景

    • 热点新闻:TTL 较短(15 分钟)
    • 普通新闻:TTL 较长(几小时)
    • 历史新闻:TTL 更长(几天)
  • 社交场景

    • 用户动态:TTL 较短(5 分钟)
    • 好友列表:TTL 较长(几小时)
    • 静态资料:长期 TTL

案例分析

1. 缓存雪崩导致数据库过载

  • 背景

    • 某电商平台在大促期间,大量商品缓存同时过期
    • 导致大量请求回源到数据库
    • 数据库 CPU 使用率达到 100%,系统响应缓慢
  • 原因分析

    • 所有商品缓存设置了相同的 TTL(24 小时)
    • 大促开始时集中加载商品数据,导致 24 小时后集中过期
    • 没有设置随机过期时间,引发缓存雪崩
  • 解决方案

    1. 立即增加数据库资源,缓解压力
    2. 优化缓存 TTL 设置,添加随机值
    3. 实现缓存预热,避免集中加载
    4. 采用分层缓存架构,减轻数据库压力
    5. 建立缓存雪崩告警机制
  • 结果

    • 系统恢复正常
    • 后续大促未再出现缓存雪崩
    • 数据库负载明显降低

2. 热点数据过期导致性能波动

  • 背景

    • 某新闻平台的热点新闻缓存过期
    • 导致大量请求回源,系统响应时间从 50ms 增加到 500ms
    • 影响用户体验
  • 原因分析

    • 热点新闻设置了固定的 TTL(15 分钟)
    • 新闻发布后 15 分钟,缓存过期
    • 大量用户同时访问,导致回源压力增大
  • 解决方案

    1. 优化热点数据识别机制
    2. 对热点新闻延长 TTL(60 分钟)
    3. 实现热点数据本地缓存
    4. 增加缓存节点,分散负载
  • 结果

    • 系统响应时间恢复正常
    • 热点新闻访问稳定
    • 建立了热点数据自动识别和优化机制

常见问题(FAQ)

Q1: 如何设置 Memcached 数据永不过期?

A1: 设置 TTL 为 0 即可实现永不过期:

set mykey 0 0 5
value

注意:永不过期的数据只会在内存不足时被驱逐,或通过 flush_all 命令清除。

Q2: Memcached 过期时间的精度是多少?

A2: Memcached 过期时间的精度为秒级。实际过期时间可能会有轻微延迟,因为过期检查是周期性的,不是实时的。

Q3: 如何更新现有键的过期时间?

A3: 可以使用 touch 命令更新现有键的过期时间,不改变键值:

touch mykey 7200

也可以通过重新 set 键来更新过期时间,但会覆盖原键值。

Q4: 如何批量清除特定前缀的键?

A4: Memcached 不直接支持按前缀清除,但可以通过以下方法实现:

  1. 使用命名空间管理相关键,通过更新命名空间版本实现批量失效
  2. 遍历所有键,手动删除匹配前缀的键(仅适用于小型缓存)
  3. 使用第三方工具或客户端库提供的按前缀清除功能

Q5: 为什么过期数据仍占用内存?

A5: 因为 Memcached 使用惰性删除和主动删除相结合的机制:

  • 惰性删除:只有在访问数据时才检查过期
  • 主动删除:每 60 秒随机检查 100 个 item

过期数据会在被访问或被主动检查到时删除,释放内存。

Q6: 如何避免缓存雪崩?

A6: 避免缓存雪崩的方法:

  1. 设置随机过期时间,避免数据集中过期
  2. 采用分层缓存架构,提高系统容错能力
  3. 实现缓存预热,避免集中加载
  4. 配置合理的重试和降级机制
  5. 监控缓存命中率,及时发现异常
  6. 实现热点数据识别和特殊处理