Redis是基于内存的key-value数据库,内存的大小是有限制的,如果内存满了,Redis会怎么办呢,另外,我们可以通过expire设置key的过期时间,那么key过期失效了, Redis回收机制又是怎么样的呢?
内存回收策略
最大内存配置
Redis的按照目录下的redis.conf配置文件中可以配置redis占用内存的大小
# 设置最大占用内存为100MB
maxmemory 100mb
tips: 如果这里设置为0的话,64位的操作系统默认是没有内存限制,而32位的操作系统隐式限制为3GB
Redis还有两个关于最大内存的命令
// 获取最大内存限制
config get maxmemory
1) "maxmemory"
2) "0"
// 设置最大的内存限制
config set maxmemory 100mb
内存驱逐策略(Eviction policies)
当达到配置文件最大内存限制的时候,Redis有几种策略来处理这种情况:
如果不懂LRU算法,可以参考一下这篇文章LRU算法
- noeviction(默认策略):对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)
- allkeys-lru:从所有key中使用LRU算法进行淘汰
- volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰
- allkeys-random:从所有key中随机淘汰数据
- volatile-random:从设置了过期时间的key中随机淘汰
- volatile-ttl:在设置了过期时间的key中,根据key的过期时间进行淘汰,越早过期的越优先被淘汰
当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误
redis 支持利用命令对驱逐策略进行修改
// 获取内存淘汰策略 可以看到默认是noeviction
config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
// 修改内存驱逐策略
config set maxmemory-policy allkeys-lru
同样地,也可以在配置文件中指定驱逐策略
maxmemory-policy allkeys-lru
驱逐策略如何生效
- 当Redis收到一条会添加数据的命令
- Redis检查内存使用情况,是否超过最大内存限制
- 超过则执行内存驱逐策略,然后执行命令。
tips:如果某个命令大量使用内存,则占用内存可能会超过最大内存限制
Redis中实现的LRU算法
Redis实现的是一个近似的LRU算法,它跟常规的LRU算法还不太一样。近似LRU算法通过随机采样法淘汰数据,每次随机出5(默认)个key,从里面淘汰掉最近最少使用的key。
Redis为了实现近似LRU算法,给每个key增加了一个额外增加了一个24bit的字段,用来存储该key最后一次被访问的时间。
tips:可以通过设置值maxmemory-samples参数修改采样数量
Redis 3.0对这个近似算法的优化
新算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中最小的时间才会放入池中,直到候选池被放满。当放满后,如果有新的key需要放入,则将池中最后访问时间最大(最近被访问)的移除。
当需要淘汰时,需要从池中捞出最久没被访问的key淘汰掉就行了。
新旧算法的对比
下面的图片是Redis官方文档给出的新旧算法对比结果:
imageRedis 3.0 与 Redis 2.8 LRU内存驱逐算法对比
- 浅灰色是被淘汰的数据
- 灰色是没有被淘汰掉的老数据
- 绿色是新加入的数据
可以看到3.0的效果明显比2.8的要得多,并且取样数越大,越接近标准的LRU算法
为什么Redis不使用真正的LRU
原因很简单,理论的LRU需要你占用更大的内存(每个key还需要保存前后key的地址), 但你从上图就可以看出Redis 3.0使用的近似LRU算法使用起来的效果几乎与理论的LRU等效了。
新的驱逐策略---LFU
LFU算法是Redis4.0里面新加的一种淘汰策略。它的全称是Least Frequently Used,它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。
LFU算法能更好的表示一个key被访问的热度。假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热点数据。
实现主要依靠于两个参数计算器和衰减周期,计算器取值范围为0-255,访问次数越高对应的取值越高,衰减周期表示每过一定的时间,就对计算器进行减值。有如下两个参数
// 因子越大,改要使频率计数器所需的次数越多
lfu-log-factor 10
// 每隔1分钟计算器减1, 如果是0的话表示每次扫描时总是使计数器衰减
lfu-decay-time 1
LFU一共有两种策略:
- volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key
- allkeys-lfu:在所有的key中使用LFU算法淘汰数据
key失效机制
redis的Key失效机制有两种:被动方式(passive way)和主动方式(active way)
被动方式
当客户端尝试访问key时,如果发现key已经失效了,就删除该key,并且告诉客户端该key已经失效了。
主动方式
当然,有被动方式还不够,因为可能会存在一些过期的key却不会被再次访问。
那怎么删除这些key呢,定时遍历整个库吗?这样当然不行,数据量大的时候太过于耗费性能了。
Redis主动删除失效key的策略是:随机抽取一部分的key进行校验,如果已经失效,就删除淘汰。
具体措施
Redis每秒执行以下操作10次:
- 在有过期时间的key集合中随机抽取20个key。
- 删除所有的过期key
- 如果过期的key超过25%,重新执行步骤1
作者:巽竹
链接:https://juejin.im/post/5eeb92b06fb9a058a42d0cd0
来源:掘金
网友评论