Skip to content

Redis 过期策略

什么是过期策略?

Redis 过期策略是指用于管理具有生存时间(TTL)的键以及在达到内存限制时驱逐键的机制。它包括:

  • TTL管理:Redis 如何处理具有过期时间的键
  • 驱逐策略:当内存满时,Redis 如何选择要删除的键
  • 内存管理:Redis 如何优化内存使用

核心概念

  • TTL(生存时间):键将被自动删除的时间
  • 过期:当键的 TTL 到期时删除键的过程
  • 驱逐:当达到内存限制时,删除键以释放内存的过程
  • Maxmemory:Redis 可以使用的最大内存量
  • 驱逐策略:用于选择要驱逐的键的算法

TTL管理

设置TTL

使用EX/EXAT选项

bash
# 设置秒级TTL
redis-cli set key value EX 3600

# 使用绝对时间戳设置TTL(自纪元以来的秒数)
redis-cli set key value EXAT 1672531200

使用PX/PXAT选项

bash
# 设置毫秒级TTL
redis-cli set key value PX 3600000

# 使用绝对时间戳设置TTL(自纪元以来的毫秒数)
redis-cli set key value PXAT 1672531200000

使用EXPIRE/PEXPIRE命令

bash
# 为现有键设置秒级TTL
redis-cli expire key 3600

# 为现有键设置毫秒级TTL
redis-cli pexpire key 3600000

使用EXPIREAT/PEXPIREAT命令

bash
# 设置绝对过期时间(自纪元以来的秒数)
redis-cli expireat key 1672531200

# 设置绝对过期时间(自纪元以来的毫秒数)
redis-cli pexpireat key 1672531200000

检查TTL

bash
# 检查秒级TTL(-1 = 无过期时间,-2 = 键不存在)
redis-cli ttl key

# 检查毫秒级TTL
redis-cli pttl key

# 检查键是否有过期时间
redis-cli exists key
redis-cli ttl key > 0

移除TTL

bash
# 从键中移除过期时间(使其持久化)
redis-cli persist key

TTL实现机制

  • 过期字典:Redis 维护一个单独的字典,将键映射到其过期时间
  • 惰性过期:当访问键时,Redis 检查它是否已过期,如果需要则删除它
  • 主动过期:Redis 定期扫描带有过期时间的键并删除已过期的键

驱逐策略

Maxmemory配置

txt
# 设置最大内存使用量
maxmemory 4gb

# 设置驱逐策略
maxmemory-policy volatile-lru

可用的驱逐策略

仅对设置了过期时间的键生效的策略

  • volatile-lru: 驱逐设置了过期时间的最近最少使用的键
  • volatile-lfu: 驱逐设置了过期时间的最不经常使用的键 (Redis 4.0+)
  • volatile-ttl: 驱逐设置了过期时间且剩余生存时间最短的键
  • volatile-random: 随机驱逐设置了过期时间的键

对所有键生效的策略

  • allkeys-lru: 驱逐最近最少使用的键,无论是否设置了过期时间
  • allkeys-lfu: 驱逐最不经常使用的键,无论是否设置了过期时间 (Redis 4.0+)
  • allkeys-random: 随机驱逐任何键

不驱逐策略

  • noeviction: 当内存限制达到时返回错误(4.0版本之前的默认策略)

驱逐过程

  1. 内存检查:Redis 检查内存使用是否超过 maxmemory
  2. 策略选择:Redis 根据配置的策略选择要驱逐的键
  3. 键移除:Redis 删除选定的键并释放内存
  4. 客户端通知:如果配置了,Redis 会为删除的键发送键空间通知

驱逐算法详情

LRU (最近最少使用)

  • 维护一个伪 LRU 算法(出于性能考虑不是精确的 LRU)
  • 使用采样方法:随机采样少量键并驱逐其中最近最少使用的一个
  • 可配置采样大小:maxmemory-samples 5(默认值)

LFU (最不经常使用)

  • 统计键被访问的频率
  • 实现了一个衰减计数器来处理变化的访问模式
  • 两个配置参数:
    • lfu-log-factor:控制访问记录的方式(默认值:10)
    • lfu-decay-time:控制计数器衰减的速度(默认值:1)

TTL (生存时间)

  • 驱逐剩余生存时间最短的键
  • 使用与 LRU 类似的采样方法
  • 适用于会话管理和时间敏感数据

内存优化

计算内存使用

bash
# 检查当前内存使用情况
redis-cli info memory

# 检查特定键的内存使用情况
redis-cli memory usage key

# 查找大键
redis-cli --bigkeys

优化TTL使用

  • 设置适当的TTL:不要设置太长或太短的TTL
  • 为临时数据使用TTL:会话、缓存条目、临时锁
  • 避免对持久数据设置TTL:不应自动删除的关键数据
  • 批量TTL操作:对多个键设置TTL时使用流水线

监控过期和驱逐

bash
# 检查驱逐统计信息
redis-cli info stats | grep evicted_keys

# 检查过期键数量
redis-cli info stats | grep expired_keys

# 监控实时驱逐情况
redis-cli monitor | grep evict

最佳实践

选择合适的驱逐策略

使用场景推荐策略
缓存volatile-lru 或 allkeys-lru
会话存储volatile-ttl
频繁访问模式volatile-lfu 或 allkeys-lfu
随机访问模式volatile-random 或 allkeys-random
关键数据noeviction

TTL最佳实践

  • 创建键时设置TTL:在SET命令中使用EX/PX选项
  • 缓存数据避免过长的TTL:根据使用场景通常为5分钟到24小时
  • 使用TTL范围:将类似的键分组,使用相似的TTL来优化过期处理
  • 监控TTL分布:确保TTL适合您的数据

内存管理最佳实践

  • 将maxmemory设置为可用RAM的70-80%:为其他进程留出空间
  • 定期监控内存使用情况:为高内存使用设置告警
  • 使用适当的数据结构:为您的使用场景选择最内存高效的数据结构
  • 实现数据分区:使用Redis Cluster进行水平扩展
  • 定期清理过期键:虽然Redis会自动执行此操作,但如果需要,您可以使用SCAN来查找和删除过期键

常见问题及解决方案

高驱逐率

问题:Redis正在驱逐大量键

解决方案

  • 如果可能,增加maxmemory
  • 审查驱逐策略并考虑更改
  • 检查TTL设置是否适当
  • 优化数据结构以减少内存使用
  • 考虑使用Redis Cluster进行水平扩展

键未过期

问题:设置了TTL的键没有被删除

解决方案

  • 检查TTL是否正确设置:redis-cli ttl key
  • 验证键是否未被访问(惰性过期)
  • 检查主动过期配置:redis-cli config get hz
  • 增加hz参数使主动过期更频繁

内存使用超过Maxmemory

问题:Redis内存使用超过配置的maxmemory

解决方案

  • 检查是否启用了驱逐:redis-cli config get maxmemory-policy
  • 确保maxmemory设置正确:redis-cli config get maxmemory
  • 如果使用volatile策略,验证键是否设置了过期时间
  • 如果合适,考虑更改为allkeys策略

过期和驱逐对性能的影响

问题:过期和驱逐导致性能问题

解决方案

  • 针对您的工作负载优化hz参数
  • 调整maxmemory-samples以平衡准确性和性能
  • 为您的访问模式使用适当的驱逐策略
  • 实现分片以分散驱逐负载

配置示例

基本配置

txt
# 设置maxmemory为4GB
maxmemory 4gb

# 对设置了过期时间的键使用LRU驱逐
maxmemory-policy volatile-lru

# 设置驱逐采样大小
maxmemory-samples 5

LFU配置

txt
# 使用LFU驱逐
maxmemory-policy volatile-lfu

# 设置LFU参数
lfu-log-factor 10
lfu-decay-time 1

会话存储配置

txt
# 对会话存储使用基于TTL的驱逐
maxmemory-policy volatile-ttl

# 为会话存储设置maxmemory为2GB
maxmemory 2gb

监控和告警

要监控的关键指标

  • used_memory: 当前内存使用量
  • used_memory_rss: Redis进程使用的RSS内存
  • mem_fragmentation_ratio: 内存碎片率
  • evicted_keys: 由于maxmemory而被驱逐的键数量
  • expired_keys: 过期的键数量
  • keyspace_hits: 成功的键查找次数
  • keyspace_misses: 失败的键查找次数

设置告警

  • 内存使用: 当内存使用超过maxmemory的80%时告警
  • 驱逐率: 当驱逐率高时告警
  • 碎片率: 当碎片率超过1.5时告警
  • 键空间未命中率: 当未命中率高时告警

常见问题(FAQ)

Q1: Redis 如何实现 LRU?

A1: Redis 出于性能考虑使用伪 LRU 算法。它维护一个键的样本集,并从样本中驱逐最近最少使用的键。这种方法在提供良好性能的同时近似了真正的 LRU 行为。样本大小可以通过 maxmemory-samples 参数配置。

Q2: LRU 和 LFU 有什么区别?

A2: LRU(最近最少使用)驱逐最长时间未被访问的键,而 LFU(最不经常使用)驱逐访问频率最低的键。LFU 更适合具有稳定访问模式的工作负载,其中一些键被频繁访问,而其他键很少访问。LRU 更适合具有变化访问模式的工作负载。

Q3: Redis 多久检查一次过期键?

A3: Redis 通过两种方式检查过期键:

  1. 惰性过期:当访问键时,Redis 检查它是否已过期,如果需要则删除它
  2. 主动过期:Redis 定期扫描部分键空间,查找并删除已过期的键。频率由 hz 参数控制(默认值:10,意味着每秒 10 次)

Q4: 我可以在运行时更改驱逐策略吗?

A4: 是的,您可以在运行时更改驱逐策略而无需重启 Redis:

bash
redis-cli config set maxmemory-policy volatile-lru

此更改将立即生效,但除非您更新 redis.conf 文件,否则重启后将丢失。

Q5: 当 Redis 使用 noeviction 策略耗尽内存时会发生什么?

A5: 当 Redis 使用 noeviction 策略耗尽内存时,它将为任何会增加内存使用的写操作返回错误。读操作将继续正常工作。此策略推荐用于不应自动删除的关键数据。

Q6: 如何估算 Redis 实例所需的内存?

A6: 要估算内存需求:

  1. 计算数据大小:键数量 × 平均键大小
  2. 增加开销:Redis 进程、客户端缓冲区等通常需要 20-30% 的开销
  3. 考虑增长:乘以增长因子(1.5-2.0)
  4. 将 maxmemory 设置为可用 RAM 的 70-80%,为其他进程留出空间