Redis数据过期和淘汰策略

在Redis中过期数据的删除有三种方式:

  1. 定期删除

redis默认每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果有过期就删除。注意这里是随机抽取的。为什么要随机呢?假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载。

  1. 惰性删除

定期删除可能导致很多过期的key 到了时间并没有被删除掉。这时就要使用到惰性删除。当读写一个已经过期的key时,会触发redis删除这个过期key。

  1. 主动删除

redis可配置 maxmemory -> 当使用的内容超过了maxmemory时,会触发主动清理策略。

3种方式同时存在。

数据淘汰策略

Redis是基于内存的key-value数据库,因为系统的内存大小有限,所以在使用Redis的时候可以配置Redis能使用的最大的内存大小。

1
2
3
4
# 设置最大可使用内存大小
# maxmemory <bytes>
# 设置数据淘汰策略
# maxmemory-policy noeviction

默认maxmemory没有设置,即Redis最大可使用内存受系统内存限制。

当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap)。交换会让 Redis 的性能急剧下降,在生产环境中我们是不允许 Redis 出现交换行为的,用参数 maxmemory 来限制内存超出期望大小。 当实际内存超出 maxmemory 时,Redis提供了几种可选策略 (maxmemory-policy) 来让决定该如何腾出新的空间以继续提供读写服务。

  • noeviction: 不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
  • volatile-lru 尝试淘汰设置了过期时间的 key,最少使用的 key 优先被淘汰。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。
  • volatile-ttl 跟上面一样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰。
  • volatile-random 跟上面一样,不过淘汰的 key 是过期 key 集合中随机的 key。
  • allkeys-lru 区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不只是过期的 key 集合。这意味着没有设置过期时间的 key 也会被淘汰。
  • allkeys-random 跟上面一样,不过淘汰的策略是随机的 key。

如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

获取当前内存淘汰策略

1
127.0.0.1:6379> config get maxmemory-policy

修改淘汰策略

  • 通过配置文件设置淘汰策略
1
maxmemory-policy allkeys-lru
  • 通过命令修改淘汰策略
1
127.0.0.1:6379> config set maxmemory-policy allkeys-lru

LRU算法

Redis的机制数据淘汰策略,使用了LRU算法,关于LRU算法实现可参考文章:LRU算法实现。不过Redis使用的是近似LRU算法,它跟常规的LRU算法还不太一样。近似LRU算法通过随机采样法淘汰数据,每次随机出5(默认)个key,从里面淘汰掉最近最少使用的key。

可以通过maxmemory-samples参数修改采样数量:

1
2
3
4
# The default of 5 produces good enough results. 10 Approximates very closely
# true LRU but costs a bit more CPU. 3 is very fast but not very accurate.
#
# maxmemory-samples 5

maxmenory-samples配置的越大,选项配置越大,消耗时间就越长,但结构也就越精准,淘汰的结果越接近于严格的LRU算法。

Redis为了实现近似LRU算法,给每个key增加了一个额外增加了一个24bit的字段,用来存储该key最后一次被访问的时间。

1
2
3
4
5
typedef struct redisObject {
...
unsigned lru:24;
...
} robj;

当缓存对象被访问时,便会更新此字段的值。

有用就打赏一下作者吧!