Redis数据过期和淘汰策略
在Redis中过期数据的删除有三种方式:
- 定期删除
redis默认每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果有过期就删除。注意这里是随机抽取的。为什么要随机呢?假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载。
- 惰性删除
定期删除可能导致很多过期的key 到了时间并没有被删除掉。这时就要使用到惰性删除。当读写一个已经过期的key时,会触发redis删除这个过期key。
- 主动删除
redis可配置 maxmemory -> 当使用的内容超过了maxmemory时,会触发主动清理策略。
3种方式同时存在。
# 数据淘汰策略
Redis是基于内存的key-value数据库,因为系统的内存大小有限,所以在使用Redis的时候可以配置Redis能使用的最大的内存大小。
# 设置最大可使用内存大小
# maxmemory <bytes>
# 设置数据淘汰策略
# maxmemory-policy noeviction
2
3
4
默认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(不删除) 基本上一致。
# 获取当前内存淘汰策略
127.0.0.1:6379> config get maxmemory-policy
# 修改淘汰策略
- 通过配置文件设置淘汰策略
maxmemory-policy allkeys-lru
- 通过命令修改淘汰策略
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参数修改采样数量:
# 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
2
3
4
maxmenory-samples配置的越大,选项配置越大,消耗时间就越长,但结构也就越精准,淘汰的结果越接近于严格的LRU算法。
Redis为了实现近似LRU算法,给每个key增加了一个额外增加了一个24bit的字段,用来存储该key最后一次被访问的时间。
typedef struct redisObject {
...
unsigned lru:24;
...
} robj;
2
3
4
5
当缓存对象被访问时,便会更新此字段的值。