1. Redis过期删除与内存淘汰的本质区别
Redis作为内存数据库,面临两个核心问题:如何清理过期数据?内存不足时如何淘汰数据?这是两个完全不同的机制,但很多开发者容易混淆。
过期删除策略针对的是那些已经设置了过期时间(TTL)的键值对。Redis会在这些键过期后自动删除它们,这是Redis提供的一种主动清理机制。而内存淘汰策略是当Redis内存使用达到maxmemory限制时,按照特定算法选择一些键进行删除以释放空间。
关键区别:过期删除是"时间驱动"的,内存淘汰是"空间驱动"的。
举个例子,假设Redis中有一个键user:1001:session设置了30分钟过期时间。30分钟后,即使内存还很充足,Redis也会删除这个键——这是过期删除。而如果Redis内存快满了,可能根据LRU算法删除最近最少使用的键product:9988:cache,即使它的过期时间还没到——这就是内存淘汰。
2. Redis的过期删除策略实现细节
2.1 惰性删除(Lazy Expiration)
当客户端尝试访问一个键时,Redis会先检查该键是否已过期。如果过期就立即删除,然后返回空值。这种策略的优点是节省CPU资源,只在必要时才检查过期。
但缺点也很明显:如果大量过期键长期不被访问,它们会一直占用内存。我曾经在生产环境遇到一个案例:某电商系统每天生成数百万临时促销键,设置24小时过期。由于促销结束后无人访问这些键,导致Redis内存持续增长,直到触发内存淘汰。
2.2 定期删除(Active Expiration)
Redis会每隔一段时间(默认100ms)随机抽取一定数量的键(默认20个)检查过期情况。通过调整hz参数可以改变检查频率,但过高会影响性能。
定期删除采用自适应算法:如果发现过期键比例超过25%,会立即进行下一轮检查。这种策略是对惰性删除的补充,但仍然无法保证完全及时清理。
实战建议:对于过期键密集的场景,可以适当调高
hz值(如20),但需要监控CPU使用率。
3. Redis内存淘汰策略详解
当内存达到maxmemory限制时,Redis提供了8种淘汰策略:
3.1 不淘汰策略
noeviction(默认):拒绝所有写入请求,只允许读操作。适合数据绝对不能丢失的场景。
3.2 仅淘汰过期键
volatile-random:随机删除设置了过期时间的键volatile-ttl:优先删除剩余生存时间(TTL)最短的键volatile-lru:删除最近最少使用的过期键volatile-lfu:删除访问频率最低的过期键
3.3 淘汰所有键
allkeys-random:随机删除任意键allkeys-lru:删除最近最少使用的键allkeys-lfu:删除访问频率最低的键
4. LRU与LFU算法的实现原理
4.1 LRU(最近最少使用)算法
Redis采用近似LRU算法,通过采样而非全量扫描来平衡性能与精度。关键参数maxmemory-samples(默认5)控制采样数量。
算法过程:
- 维护一个淘汰候选池(大小16)
- 每次需要淘汰时,随机选取
maxmemory-samples个键 - 将其中最久未访问的键放入候选池
- 从候选池中淘汰真正的LRU键
# 简化的LRU实现 class LRUCache: def __init__(self, capacity): self.capacity = capacity self.cache = OrderedDict() def get(self, key): if key not in self.cache: return -1 self.cache.move_to_end(key) return self.cache[key] def put(self, key, value): if key in self.cache: self.cache.move_to_end(key) self.cache[key] = value if len(self.cache) > self.capacity: self.cache.popitem(last=False)4.2 LFU(最不经常使用)算法
Redis 4.0引入LFU算法,考虑访问频率而非最近访问时间。每个键维护一个计数器:
- 每次访问时计数器递增
- 计数器会随时间衰减
LFU更适合热点数据明显的场景,比如新闻热点排行榜。配置示例:
maxmemory-policy allkeys-lfu lfu-log-factor 10 lfu-decay-time 15. 生产环境配置建议
5.1 策略选择指南
- 数据重要性高且可预测访问模式:
volatile-lru - 需要保留最新数据:
allkeys-lru - 存在明显热点数据:
allkeys-lfu - 数据完全不可丢失:
noeviction+扩容
5.2 关键参数调优
# 内存限制(根据机器内存的70-80%设置) maxmemory 16gb # 采样数量(提高精度但增加CPU开销) maxmemory-samples 10 # LFU衰减系数(值越大衰减越慢) lfu-log-factor 10 # 主动删除频率(默认10,范围1-500) hz 205.3 监控指标
used_memory:当前内存使用量evicted_keys:累计淘汰键数expired_keys:累计过期键数keyspace_hits/misses:缓存命中率
6. 常见问题排查案例
6.1 内存突然增长
现象:Redis内存使用率突然飙升 排查步骤:
- 检查
info memory中的used_memory变化 - 分析
slowlog是否有大键写入 - 使用
redis-cli --bigkeys查找大键 - 确认是否大量键设置了较长TTL
6.2 频繁淘汰导致性能下降
现象:QPS下降,evicted_keys快速增长 解决方案:
- 调整淘汰策略为
allkeys-lru - 增加
maxmemory-samples提高淘汰精度 - 考虑水平分片或升级硬件
6.3 缓存穿透问题
现象:大量请求直接打到数据库 解决方案:
- 对不存在的键也缓存空值(设置较短TTL)
- 使用布隆过滤器预判键是否存在
- 实现客户端本地缓存
在电商大促期间,我们曾通过组合使用allkeys-lfu策略和本地缓存,将缓存命中率从75%提升到98%,数据库负载降低80%。关键是在压测阶段就模拟真实流量模式,验证不同策略的效果。