Redis 过期和淘汰策略
过期策略
过期的 key 集合
redis 会将每个设置了过期时间的 key
放入到一个独立的字典中,以后会定时遍历这个字典来删除到期的 key
。除了定时遍历之外,它还会使用惰性策略来删除过期的 key
,所谓惰性策略就是在客户端访问这个 key
的时候,redis 对 key
的过期时间进行检查,如果过期了就立即删除。定时删除是集中处理,惰性删除是零散处理。
定时扫描策略
Redis 默认会每秒进行十次过期扫描,过期扫描不会遍历过期字典中所有的 key
,而是采用了一种简单的贪心策略。
- 从过期字典中随机 20 个
key
; - 删除这 20 个
key
中已经过期的key
; - 如果过期的
key
比率超过 1/4,那就重复步骤 1;
同时,为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过 25ms。
业务开发人员一定要注意过期时间,如果有大批量的 key
过期,要给过期时间设置一个随机范围,而不宜全部在同一时间过期,分散过期处理的压力:
# 在目标过期时间上增加一天的随机时间
redis.expire_at(key, random.randint(86400) + expire_ts)
从库的过期策略
从库不会进行过期扫描,从库对过期的处理是被动的。主库在 key
到期时,会在 AOF 文件里增加一条 del
指令,同步到所有的从库,从库通过执行这条 del
指令来删除过期的 key
。
因为指令同步是异步进行的,所以主库过期的 key
的 del
指令没有及时同步到从库的话,会出现主从数据的不一致,主库没有的数据在从库里还存在,比如集群环境分布式锁的算法漏洞就是因为这个同步延迟产生的。
超出内存的淘汰策略
当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap)。交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样龟速的存取效率基本上等于不可用。
在生产环境中我们是不允许 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。