外观
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 keyTTL实现机制
- 过期字典: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版本之前的默认策略)
驱逐过程
- 内存检查:Redis 检查内存使用是否超过 maxmemory
- 策略选择:Redis 根据配置的策略选择要驱逐的键
- 键移除:Redis 删除选定的键并释放内存
- 客户端通知:如果配置了,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 5LFU配置
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 通过两种方式检查过期键:
- 惰性过期:当访问键时,Redis 检查它是否已过期,如果需要则删除它
- 主动过期: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: 要估算内存需求:
- 计算数据大小:
键数量 × 平均键大小 - 增加开销:Redis 进程、客户端缓冲区等通常需要 20-30% 的开销
- 考虑增长:乘以增长因子(1.5-2.0)
- 将 maxmemory 设置为可用 RAM 的 70-80%,为其他进程留出空间
