1. 引言
Redis 作为一款高性能的内存数据库,其数据全部存储在内存中。当内存使用量达到上限时,Redis 需要采取一定的策略来淘汰部分数据,以保证服务的正常运行。本文将详细介绍 Redis 的内存淘汰机制、常见策略及其适用场景。
2. 为什么需要内存淘汰策略
Redis 默认情况下可以无限使用服务器内存,但当内存达到物理内存上限时,操作系统会使用交换分区(Swap),导致 Redis 性能急剧下降。因此,合理配置内存上限和淘汰策略至关重要。我们要清理掉无用的缓存,提高我们系统的运行速度。
通过设置maxmemory参数可以限制 Redis 最大可用内存。当实际内存超过该限制时,Redis 会根据配置的淘汰策略(maxmemory-policy)来决定如何处理新写入请求。
3. 内存淘汰策略分类
Redis 提供了多种内存淘汰策略,主要分为「不淘汰」和「主动淘汰」两大类。主动淘汰策略又根据是否考虑访问频率(LRU/LFU)以及作用范围(全库/设置了过期时间的键)进行细分。
3.1 不淘汰策略
- noeviction:默认策略。当内存达到上限时,新写入操作会返回错误,但读操作和删除操作不受影响。适用于数据不可丢失、必须保证写入成功的场景。这个策略很少的运用在我们的业务中,一般的业务都会需要去清理掉过期的键。
3.2 基于 LRU 的淘汰策略
LRU(Least Recently Used,最近最少使用)算法根据数据的访问时间进行淘汰,优先移除最久未被访问的键。这个策略是一个很好的办法,因为很少访问的键实际上需求也很小,那么此时将这个键移除的话,即使后面需要用到这个,那也是很小的概率,去数据库中查找也不会消耗太多的性能,这样一来就可以把内存留给真正需要的部分。
- allkeys-lru:在所有键中按照 LRU 算法淘汰,不区分是否设置了过期时间。
- volatile-lru:仅在设置了过期时间的键中按照 LRU 算法淘汰。适用于需要保留未设置过期时间的长期数据、只淘汰临时数据的场景。
3.3 基于 LFU 的淘汰策略
LFU(Least Frequently Used,最不经常使用)算法根据数据的访问频率进行淘汰,优先移除访问次数最少的键。Redis 4.0 开始支持该策略。这个本质上和LRU淘汰策略是一样的,都是基于使用的时间或者频率来进行决定的。
- allkeys-lfu:在所有键中按照 LFU 算法淘汰。
- volatile-lfu:仅在设置了过期时间的键中按照 LFU 算法淘汰。
3.4 随机淘汰策略
这个方法在我们的业务中基本上用不到,随机淘汰是不可控的,我们的业务是比较复杂的,并不是单一的业务,有一些键是很重要的,我们要一直保留,随机淘汰则会删除掉这个键,导致后面出现一系列问题,因此在实际中我们更倾向于更可控的一个方法去清理内存。
- allkeys-random:在所有键中随机淘汰。
- volatile-random:仅在设置了过期时间的键中随机淘汰。
3.5 基于过期时间的淘汰策略
- volatile-ttl:仅在设置了过期时间的键中,优先淘汰剩余生存时间(TTL)最短的键。
这个方法也是一个很好的方法,但是如果单独使用的话则会出现很多问题,比如现在有一个人正在进行秒杀活动,他此时抢到了这个线程,他在线程结束的最后一秒准备下单的时候,突然他被清理了,那么此时就会出现所谓的bug,这是我们需要考虑并且在实际生产中进行避免的一个东西。
4. 策略对比与选择建议
| 策略 | 作用范围 | 淘汰依据 | 适用场景 |
|---|---|---|---|
| noeviction | 全部键 | 不淘汰 | 数据不可丢失,内存充足 |
| allkeys-lru | 全部键 | 最近访问时间 | 缓存热点数据,淘汰冷数据 |
| volatile-lru | 设置了过期时间的键 | 最近访问时间 | 保留长期数据,淘汰临时缓存 |
| allkeys-lfu | 全部键 | 访问频率 | 访问频率差异明显的场景 |
| volatile-lfu | 设置了过期时间的键 | 访问频率 | 临时数据按频率淘汰 |
| allkeys-random | 全部键 | 随机 | 数据访问均匀,无热点 |
| volatile-random | 设置了过期时间的键 | 随机 | 临时数据随机淘汰 |
| volatile-ttl | 设置了过期时间的键 | 剩余 TTL | 优先淘汰即将过期的数据 |
5. 配置与实战
在 Redis 配置文件redis.conf中,可以通过以下参数进行配置:
# 设置最大内存为 256MB maxmemory 256mb 设置淘汰策略为 allkeys-lru maxmemory-policy allkeys-lru 设置采样数量(默认 5),影响 LRU/LFU 近似算法的精度 maxmemory-samples 5也可以通过命令行动态修改:
# 连接 Redis 后执行 127.0.0.1:6379> CONFIG SET maxmemory 256mb 127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lru6. 注意事项
- Redis 的 LRU 和 LFU 算法是近似实现,通过采样(
maxmemory-samples)来降低性能开销,并非严格的全量排序。 - 当设置了过期时间的键被淘汰时,会触发键过期事件通知(如果开启了
notify-keyspace-events)。 - 如果所有可淘汰的键都被移除后内存仍然超限,Redis 会退化为
noeviction行为,拒绝新写入请求。 - 建议根据业务特点选择合适的策略:缓存场景优先考虑
allkeys-lru或allkeys-lfu;需要保留长期数据的场景可考虑volatile-lru或volatile-ttl。
7. 总结
Redis 的内存淘汰策略是保障服务稳定运行的关键机制。合理配置maxmemory和maxmemory-policy,结合业务的数据访问特征选择合适的淘汰算法,可以有效提升缓存命中率,避免内存溢出和性能退化。在实际生产环境中,建议通过监控工具观察淘汰键的数量和命中率变化,持续优化策略配置。