Redis内存淘汰策略详解
1、为什么需要内存淘汰策略
我们都知道Redis是一个基于k-v数据库,随着里面键值对的增加,redis内存的使用量也会上升,因此,我们需要对Redis的最大内存使用量进行限制。那么有了Redis的内存容量限制,那么就会有达到这个内存容量限制的时候,这个时候就需要对Redis存储的数据进行清理,释放内存,将Redis内存使用量保持在容量限制以下,淘汰数据的策略当然不是盲目的,是有策略的因此需要内存淘汰策略。
2、Redis中的max-memory
在64位的系统中,Redis是没有对内存容量做限制的,但是在32位系统中默认内存容量的默认值是3GB,以下是官方原话
Setting maxmemory to zero results into no memory limits. This is the default behavior for 64 bit systems, while 32 bit systems use an implicit memory limit of 3GB.
max-memory可以在redis.conf中进行添加设置
max-memory 300mb
也可以在运行时使用命令CONFIG SET动态设置
CONFIG SET max-memory 300mb
3、淘汰内存的过程
官方原话
1、A client runs a new command, resulting in more data added.
2、Redis checks the memory usage, and if it is greater than the maxmemory limit , it evicts keys according to the policy.
3、A new command is executed, and so forth.
1、客户端执行一个新指令,添加数据
2、Redis检查内存使用量,如果大于maxmemory限制,就通过淘汰策略清理内存
3、执行新命令,重复上述过程
4、Redis中的内存淘汰策略
1、noeviction:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就返回error,然后啥也不干
2、allkeys-lru:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,淘汰一些最近未使用的key
3、volatile-lru:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,淘汰一些最近未使用的key
4、allkeys-random:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,随机淘汰一些key
5、volatile-random:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,随机淘汰一些key
6、volatile-ttl:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,扫描那些设置里过期时间的key,淘汰一些即将过期的key
7、volatile-lfu:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会淘汰一些设置了过期时间的,并且最近最少使用的key
8、allkeys-lfu:添加数据时,如果redis判断该操作会导致占用内存大小超过内存限制,就会扫描所有的key,淘汰一些最近最少使用的key
5、Redis中实现的LRU算法为近似算法
为什么说Redis实现的LRU算法是近似算法呢,官方文档有提到
Redis LRU algorithm is not an exact implementation. This means that Redis is not able to pick the best candidate for eviction, that is, the access that was accessed the most in the past. Instead it will try to run an approximation of the LRU algorithm, by sampling a small number of keys, and evicting the one that is the best (with the oldest access time) among the sampled keys.
However since Redis 3.0 the algorithm was improved to also take a pool of good candidates for eviction. This improved the performance of the algorithm, making it able to approximate more closely the behavior of a real LRU algorithm.
What is important about the Redis LRU algorithm is that you are able to tune the precision of the algorithm by changing the number of samples to check for every eviction. This parameter is controlled by the following configuration directive
以上阐述了以下观点
1、Redis中实现的LRU算法是近似算法,它是通过抽样的方式来淘汰key
2、Redis3.0以后得到了改善,该近似算法很接近原LRU算法了
3、Redis提供了一个参数让用户配置,可以使得近似LRU算法接近原LRU算法
这个参数就是maxmemory-samples
CONFIG SET maxmemory-samples <count>
通过上述指令可以设置该值,也可以在redis.conf中设置
6、Redis中的LFU算法
前面提到的LRU算法看似已经是比较好的算法了,但实际上存在一些缺陷,因为存在某些key,在最近被访问过后就不再访问了,而有些key是很久以前访问过,但是以后很可能也要访问的,如果根据LRU算法,就会导致前一类key保留下来,反而后一类真正需要的key就被淘汰了,因此LFU算法就是该问题的解决方式
LFU算法在Redis中是通过一个计数器来实现的,每个key都有一个计数器,访问频度越高,计数器的值就越大,Redis就根据计数器的值来淘汰key,当然计数器的值也是会随着时间减少的
用户可配置的LFU算法参数一共有两个分别是:lfu-log-factor和lfu-decay-time
lfu-log-factor参数值设置的越大,key的计数值就越难增长,因此就要求key的访问频度较高才能避免被淘汰
lfu-decay-time参数值是表示隔多久将计数器的值减一
下面是不同的factor情况下的计数值的增长情况,计数器不是每访问一次都会增加一,它是和factor有关系的,计数值的最大值是255,factor越大,计数器的值增长就越难,如下图
+--------+------------+------------+------------+------------+------------+
| factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits |
+--------+------------+------------+------------+------------+------------+
| 0 | 104 | 255 | 255 | 255 | 255 |
+--------+------------+------------+------------+------------+------------+
| 1 | 18 | 49 | 255 | 255 | 255 |
+--------+------------+------------+------------+------------+------------+
| 10 | 10 | 18 | 142 | 255 | 255 |
+--------+------------+------------+------------+------------+------------+
| 100 | 8 | 11 | 49 | 143 | 255 |
+--------+------------+------------+------------+------------+------------+
7、内存淘汰策略的选择(个人观点)
我们在选择使用淘汰策略的时候可以根据访问key的方式来选择不同的淘汰策略
1、当我们redis中的key基本上都有用到,也就是说每个key都有周期性访问到,那就可以选择使用random策略
2、当我们redis中的key部分是我们经常访问的,部分是非经常访问的就可以考虑使用LRU和LFU策略
3、当我们想根据时间长久淘汰超时数据时,就选用ttl
4、我们根据我们的需要是否有要长久保存的key来选择volatile或者是all,如果有需要长久保存的key,则使用volatile,否则可以使用all全表扫描