redis主要通过控制内存上限和回收策略实现内存管理
控制内存上限
redis可以通过maxmemory 参数来控制可用内存。限制内存的目的主要有:
- 用于缓存场景,当超出内存上限maxmemory时使用LRU等删除策略释放空间
- 防止所用内存超过服务器物理内存。
- 需要注意的是,maxmemory限制的是redis实际使用的内存量,也就是used_memory统计项对应的内存。
- 由于内存碎片率的存在,实际消耗的内存可能会比maxmemory设置的更大,实际使用时需要小心这部分内存溢出
动态调整内存上限
redis的内存上限可以通过config set maxmemory
进行动态修改,即修改最大可用内存,比如:
Redis-1>config set maxmemory 6GB
通过动态修改maxmemory,可以实现当前服务器下动态伸缩redis内存的目的。
注意:
- redis默认无限使用服务器内存。为防止极端情况下导致内存耗尽,建议所有的redis进程都要配置Redis
- 在保证物理内存可用的情况下,系统中所有redis实例可以通过调整maxmemory参数来达到自由伸缩内存的目的
内存回收策略
redis的内存回收机制主要体现在如下两个方面:
- 删除到达过期时间的键对象
- 内存使用达到maxmemory上限时触发内存溢出过期控制策略
关于:redis的过期策略和内存淘汰策略
一开始总会把这两个问题搞混淆了。一句话简单解释一下:
过期策略:已经过期的key删除方案。【定期删除和惰性过期】
淘汰策略:内存不够用的时候的处理方案。【就是常说的LRU算法】
Redis过期策略
我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。redis的过期策略就是指当redis中缓存的key过期了,redis如何处理
过期策略通常有三种:
- 定时过期
- 惰性过期
- 定期过期
定时过期(redis没有使用这种方法)
- redis所有的键都可以设置过期属性,内存保存在过期字典中。
- 每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。
- 该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量
- 由于进程内保存大量的键,维护每个键的精准过期删除机制会导致消耗大量的CPU,对于单线程的redis来说成本太大
- 因此redis采用惰性删除和定时任务删除机制实现过期键的内存回收,没有采用这种方法
惰性过期
- 只有当客户端访问一个key时,才会判断该key是否已经过期,过期则清除并返回空
- 这种策略是出于节省CPU成本考虑,不需要单独维护TTL链表来处理过期键的删除,但是对内存非常不友好。
- 极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存
因此,Redis还提供另一种定时任务删除机制作为惰性删除的补充。
定期过期
- redis内部维护一个定时任务,默认每秒运行20次(通过配置hz控制)。
- 每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已经过期的key
- 该策略是前两者的一个折中方案。
- 定时任务中删除过期键逻辑采用了自适应算法,根据键的过期比例,使用快慢这两种速率模式回收键。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定期遍历这个字典来删除到期的 key。
redis默认会每秒进行十次过期扫描(100ms一次),过期扫描不会遍历过期字典中所有的key,而是采用了一种简单的贪心策略
- 从过期字典中随机20个key
- 删除这30个key中已经过期的key
- 如果过期的 key 比率超过 1/4,那就重复步骤 1;
redis默认是每隔 100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载。
总结
- Redis中同时使用了惰性过期和定期过期两种过期策略。
- 定期删除是集中处理,惰性删除是零散处理。
Redis的内存淘汰策略
有了以上过期策略的说明后,就很容易理解为什么需要淘汰策略了。因为不管是定期采样删除还是惰性删除都不是一种完全精准的删除,还是会存在一个key没有被删除的场景,所以就需要内存淘汰策略进行补充。
内存淘汰策略
当redis所用内存达到maxmemory上限时会触发相应的溢出控制策略。具体策略受maxmemory-policy参数控制,Redis支持6种策略。
- noeviction:默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息
- allkeys-lru: 加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键(不管数据有没有设置超时属性),直到腾出足够空间为止
- volatile-lru:
- 加入键的时候,如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键,直到腾出足够空间为止。
- 如果没有可删除的键对象,回退到noeviction策略
- allkeys-random:加入键的时候,如果过限,随机删除所有键,直到腾出足够空间为止。
- volatile-random:加入键的时候如果过限,随机删除过期键,直到腾出足够空间为止。
- volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
- volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
- allkeys-lfu:从所有键中驱逐使用频率最少的键
内存溢出控制策略可以采用config set maxmemory-policy{policy}
动态配置。Redis支持丰富的内存溢出应对策略,可以根据实际需求灵活定制
如何配置
我们通过配置redis.conf中的maxmemory这个值来开启内存淘汰功能。
# maxmemory
值得注意的是,maxmemory为0的时候表示我们对Redis的内存使用没有限制。
根据应用场景,选择淘汰策略
# maxmemory-policy noeviction
我们来了解下,内存淘汰的过程
- 首先,客户端发起了需要申请更多内存的命令(如set)。
- 然后,Redis检查内存使用情况,如果已使用的内存大于maxmemory则开始根据用户配置的不同淘汰策略来淘汰内存(key),从而换取一定的内存。
- 最后,如果上面都没问题,则这个命令执行成功。
此外,redis支持动态改配置,无需重启。
config set maxmemory 100000
config set maxmemory-policy noeviction
总结
Redis的内存淘汰策略的选取并不会影响过期的key的处理。
- 内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;
- 过期策略用于处理过期的缓存数据。
https://www.cnblogs.com/alsf/p/9399009.html
https://www.jianshu.com/p/8aa619933ebb
https://redis.io/topics/lru-cache
https://zhuanlan.zhihu.com/p/105587132