# Redis的过期策略和内存淘汰策略

* [Redis的过期策略](#Redis的过期策略)
* [Redis对于过期键有三种清除策略](#Redis对于过期键有三种清除策略)
  * [被动删除](#被动删除)
  * [主动删除](#主动删除)
  * [当前已用内存超过maxmemory限定时，触发主动清理策略](#当前已用内存超过maxmemory限定时，触发主动清理策略)
* [查看Redis的内存淘汰策略设置](#查看Redis的内存淘汰策略设置)

## Redis的过期策略

我们都知道，Redis是key-value数据库，我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了，Redis如何处理。

Redis中同时使用了惰性过期和定期过期两种过期策略。

过期策略通常有以下三种：

* 定时过期：每个设置过期时间的key都需要创建一个定时器，到过期时间就会立即清除。该策略可以立即清除过期的数据，对内存很友好；但是会占用大量的CPU资源去处理过期的数据，从而影响缓存的响应时间和吞吐量。
* 惰性过期：只有当访问一个key时，才会判断该key是否已过期，过期则清除。该策略可以最大化地节省CPU资源，却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问，从而不会被清除，占用大量内存。
* 定期过期：每隔一定的时间，会扫描一定数量的数据库的expires字典中一定数量的key，并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时，可以在不同情况下使得CPU和内存资源达到最优的平衡效果。 (expires字典会保存所有设置了过期时间的key的过期时间数据，其中，key是指向键空间中的某个键的指针，value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)

## Redis对于过期键有三种清除策略

1、被动删除：当读/写一个已经过期的key时，会触发惰性删除策略，直接删除掉这个过期key 2、主动删除：由于惰性删除策略无法保证冷数据被及时删掉，所以Redis会定期主动淘汰一批已过期的key 3、当前已用内存超过maxmemory限定时，触发主动清理策略

## 被动删除

只有key被操作时(如GET)，REDIS才会被动检查该key是否过期，如果过期则删除之并且返回NIL。 1、这种删除策略对CPU是友好的，删除操作只有在不得不的情况下才会进行，不会对其他的expire key上浪费无谓的CPU时间。 2、但是这种策略对内存不友好，一个key已经过期，但是在它被操作之前不会被删除，仍然占据内存空间。如果有大量的过期键存在但是又很少被访问到，那会造成大量的内存空间浪费。expireIfNeeded(redisDb \*db, robj \*key)函数位于src/db.c。 但仅是这样是不够的，因为可能存在一些key永远不会被再次访问到，这些设置了过期时间的key也是需要在过期后被删除的，我们甚至可以将这种情况看作是一种内存泄露—-无用的垃圾数据占用了大量的内存，而服务器却不会自己去释放它们，这对于运行状态非常依赖于内存的Redis服务器来说，肯定不是一个好消息。

## 主动删除

先说一下时间事件，对于持续运行的服务器来说， 服务器需要定期对自身的资源和状态进行必要的检查和整理， 从而让服务器维持在一个健康稳定的状态， 这类操作被统称为常规操作（cron job）

在 Redis 中， 常规操作由 redis.c/serverCron 实现， 它主要执行以下操作

更新服务器的各类统计信息，比如时间、内存占用、数据库占用情况等。 清理数据库中的过期键值对。 对不合理的数据库进行大小调整。 关闭和清理连接失效的客户端。 尝试进行 AOF 或 RDB 持久化操作。 如果服务器是主节点的话，对附属节点进行定期同步。 如果处于集群模式的话，对集群进行定期同步和连接测试。 Redis 将 serverCron 作为时间事件来运行， 从而确保它每隔一段时间就会自动运行一次， 又因为 serverCron 需要在 Redis 服务器运行期间一直定期运行， 所以它是一个循环时间事件： serverCron 会一直定期执行，直到服务器关闭为止。

在 Redis 2.6 版本中， 程序规定 serverCron 每秒运行 10 次， 平均每 100 毫秒运行一次。 从 Redis 2.8 开始， 用户可以通过修改 hz选项来调整 serverCron 的每秒执行次数， 具体信息请参考 redis.conf 文件中关于 hz 选项的说明也叫定时删除，这里的“定期”指的是Redis定期触发的清理策略，由位于src/redis.c的activeExpireCycle(void)函数来完成。

## 当前已用内存超过maxmemory限定时，触发主动清理策略

noeviction ： 永不过期，返回错误，谁也不删，直接在写操作时返回错误。【默认淘汰策略】当内存不足以容纳新写入数据时，新写入操作会报错。

allkeys-random：随机删除，无差别随机删。当内存不足以容纳新写入数据时，在键空间中，随机移除某个key。 allkeys-lru ： 删除lru算法的key，根据LRU算法删除任何key。当内存不足以容纳新写入数据时，在键空间中，移除最近最少使用的key。 allkeys-lfu : 从所有键中驱逐使用频率最少的键

volatile-random：随机删除即将过期key，根据过期设置来随机删除key。当内存不足以容纳新写入数据时，在设置了过期时间的键空间中，随机移除某个key。 volatile-ttl ： 删除即将过期的，根据最近过期时间来删除（辅以TTL），当内存不足以容纳新写入数据时，在设置了过期时间的键空间中，有更早过期时间的key优先移除。 volatile-lru：只对设置了过期时间的key进行LRU（默认值），根据LRU算法生成的过期时间来删除。当内存不足以容纳新写入数据时，在设置了过期时间的键空间中，移除最近最少使用的key。 volatile-lfu：从所有配置了过期时间的键中驱逐使用频率最少的键

LFU是在Redis4.0后出现的 volatile-lfu：从所有配置了过期时间的键中驱逐使用频率最少的键 allkeys-lfu：从所有键中驱逐使用频率最少的键

当mem\_used内存已经超过maxmemory的设定，对于所有的读写请求，都会触发redis.c/freeMemoryIfNeeded(void)函数以清理超出的内存。注意这个清理过程是阻塞的，直到清理出足够的内存空间。所以如果在达到maxmemory并且调用方还在不断写入的情况下，可能会反复触发主动清理策略，导致请求会有一定的延迟。

清理时会根据用户配置的maxmemory-policy来做适当的清理（一般是LRU或TTL），这里的LRU或TTL策略并不是针对redis的所有key，而是以配置文件中的maxmemory-samples个key作为样本池进行抽样清理。

## 查看Redis的内存淘汰策略设置

config get maxmemory-policy

参考 <https://blog.csdn.net/FYWT98/article/details/82585583> [Redis的缓存淘汰策略LRU与LFU](https://www.jianshu.com/p/c8aeb3eee6bc)\
[彻底弄懂Redis的内存淘汰策略](https://zhuanlan.zhihu.com/p/105587132)\
[Redis的过期策略和内存淘汰策略](https://www.jianshu.com/p/8aa619933ebb)\
[redis内存淘汰策略及如何配置](https://cloud.tencent.com/developer/article/1699610)
