Redis是基于内存操作的非关系型数据库,Redis中提供了多种内存回收策略,当内存容量不足时,为了保证程序的运行,这时就不得不淘汰内存中的一些对象,释放这些对象占用的空间,那么选择淘汰哪些对象呢?
Redis的内存回收,主要围绕如下两个方面来进行:
①Redis过期策略:删除已经过期的数据;
②Redis淘汰策略:内存使用到达maxmemory上限时触发内存淘汰数据。
Redis的过期策略和淘汰策略是两个不同的概念,接下来将分开讲解这两种策略
1.Redis过期策略
在Redis中,提供了expire命令设置一个键的过期时间,到期之后Redis会自动删除它,这个在我们的实际使用过程中用的非常多,Redis中设置过期时间有如下两种方式:
expire命令:expire key seconds(先set key,然后设置过期时间。其中seconds 参数表示键的过期时间,单位为秒。expire 返回值为1表示设置成功,0表示设置失败或者键不存在)
②setex命令:setex key seconds value(设置键的同时,直接设置过期时间)
pexpire命令,pexpire命令的单位是毫秒。pexpire key 1000 与expire key 1 相等
Redis键值过期删除原理
Redis 删除失效主键的方法主要有两种:
①消极方法(passive way):
在主键被访问时,如果发现它已经失效,那么就删除它。此处有一个问题就是:对于那些从未被查询过的key,即使它们已经过期,该方法也无法删除该过期key
②积极方法(active way):
周期性的从设置了失效时间的 key 中,选择一部分失效的 key 进行删除操作。因此Redis会周期性的随机测试一些key,已过期的key将会被删除。Redis每秒会进行10次操作,根据键的过期比例,使用快慢两种速率回收键,具体的流程:
1.随机的测试20个带有timeout(过期时间)信息的key;
2.删除其中已经过期的key;
3.如果超过25%的key被删除,则重复执行步骤1
2.Redis淘汰策略
Redis中提供了多种内存回收策略,当内存容量不足时,为了保证程序的运行,这时就不得不淘汰内存中的一些对象,释放这些对象占用的空间。
Redis的淘汰策略,是指当内存使用达到maxmemory极限时,需要使用LAU淘汰算法来决定清理掉哪些数据,以保证新数据的存入,那么选择淘汰哪些对象呢?
maxmemory用来设置redis存放数据的最大的内存大小,一旦超出这个内存大小之后,就会立即使用Redis的淘汰策略,来清理掉部分数据。
对于64 bit的机器,如果maxmemory设置为0,那么就默认不限制内存的使用,直到耗尽机器中所有的内存为止;,但是对于32 bit的机器,有一个隐式的闲置就是3GB。
在Redis的配置文件redis.conf中,我们能够找到下面这样一段话:
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# 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
#
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
#
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
#
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy noeviction
LRU 表示最近最少使用
LFU 表示最不常用
我们可以看到,Redis淘汰默认的是noeviction策略,即当内存达到阈值的时候,所有引起申请内存的命令会报错。如果我们有其他需求,可以通过修改maxmemory-policy属性,来修改Redis淘汰键值使用的策略。那么有哪些淘汰策略供我们选择呢?Redis都在redis.conf配置文件中给我们罗列出来了。共有如下8种淘汰策略:
1.volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,使用LRU算法,移除最近最少使用的key;
2.allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,使用LRU算法,移除最近最少使用的key;
3.volatile-lfu:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,使用LFU算法,移除最近最少使用的key;
4.allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,使用LFU算法,移除最近最少使用的key;
5.volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key;
6.allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key;
7.volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除;
(默认):当内存不足以容纳新写入数据时,新写入操作会报错。
淘汰策略执行步骤:
1.客户端执行数据写入操作;
2.redis server接收到写入操作之后,检查maxmemory的限制,如果超过了限制,那么就根据对应的policy清理掉部分数据;
3.写入操作完成执行
总结:
实际上Redis实现的LRU并不是可靠的LRU,也就是名义上我们使用LRU算法淘汰内存数据,但是实际上被淘汰的键并不一定是真正的少使用的数据,这里涉及到一个权衡的问题,如果需要在所有的数据中搜索符合条件的数据,那么一定会增加系统的开销,Redis是单线程的,所以耗时的操作会谨慎一些。为了在一定成本内实现相对的LRU,早期的Redis版本是基于采样的LRU,也就是放弃了从所有数据中搜索解改为采样空间搜索优解。Redis3.0 版本之后,Redis作者对于基于采样的LRU进行了一些优化,目的是在一定的成本内让结果更靠近真实的LRU
END