Redis 中都是键值对的存储形式,键都是字符串类型的,而值有很多种类型,如 string、list、hash、set、sorted set等类型。当设置键值对时我们还应该为其设置过期时间,通过 expire 以及 pexpire 命令;还可以通过 setnx 命令设置。那么,当设置过期时间之后,到底是怎么将过期的键值进行删除的呢?同时redis的内存有限的情况下内存下或者说redis的内存使用完了,接下来的redis存入新的数据如何进行取舍呢?
一、接下来我们首先看看redis的内存淘汰策略?
1.redis官方提供了8种不同的淘汰策略
volatile-lru:在设置过期时间的数据中淘汰最少使用的数据。
allkeys-lru:在所有的数据中淘汰最少使用的数据。
volatile-lfu:在设置过期时间的数据中淘汰使用频率最低的数据。
allkeys-lfu:在所有的数据中淘汰使用使用频率最低的数据。
volatile-random:在设置过期时间的数据中淘汰任意随机数据。
allkeys-random:在所有的数据中随机淘汰数据。
volatile-ttl:在设置过期时间的数据中淘汰最早过期的数据。
noeviction:默认策略,不淘汰数据,新增或者修改数据会抛异常,但是读操作正常进行,不受影响
2.在redis内存不足的淘汰策略
redis.conf 包含redis的所有配置都可以在这里找到,根据conf中的说明也就能操作了 可以配置具体使用哪一种淘汰策略。
volatile-lru -> Evict using approximated LRU among the keys with an expire set.
allkeys-lru -> Evict any key using approximated LRU.
volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
allkeys-lfu -> Evict any key using approximated LFU.
volatile-random -> Remove a random key among the ones with an expire set.
allkeys-random -> Remove a random key, any key.
volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
noeviction -> Don't evict anything, just return an error on write operations.
# LRU means Least Recently Used(最近最少使用的,时间)
# LFU means Least Frequently Used(最不经常使用的,次数)
# The default is:
maxmemory-policy noeviction
其中maxmemory-policy noeviction
代表了淘汰策略默认的是noeviction
,我们可以根据自己的业务需求修改合适的策略
二、接下来我们首在看看redis的过期删除策略?
过期顾名思义就是redis对应的键值过了时效,比如 EXPIRE runooobkey 60
- 定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
- 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
- 定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
Redis实际使用的是惰性删除+定期删除的策略:通过这两种方式可以很好的利用CPU时间以及避免内存浪费的情况。接下来讲讲惰性删除以及定期删除的实现。
惰性删除策略的实现
由 expireIfNeeded 函数实现,所有读写数据库的 Redis 命令在执行之前都会调用 exipreIfNeeded 函数对输入键进行检查。
- 如果键过期,会将键删除并返回空。
- 如果键没有过期,则不做操作。
定期删除策略的实现
定期删除策略由 activeExpireCucle 函数实现,被调用时,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的 expires 字典中随机检查一部分键的过期时间,并删除其中的过期键。
- 函数每次运行时,都是从一定数量的数据库键中随机取一定数量的键进行检查,并删除其中的过期键。
- 有一个全局变量 current_db 会记录当前 activeExpireCycle 函数检查的进度,并且下一次 函数执行时,接着上一次的进度进行处理。如,当前 activeExpireCycle 函数执行到了 10, 讲 current_db = 10;然后下一次函数执行时,从 current_db 取到 10 继续执行。
- 当所有的数据库键都被检查完时, current_db = 0。