Redis进阶:内存回收策略
今天的内容很偏理论,不过也只是对于官方文档的一个补充而已,所以大家也不必有很大的心理负担。理论的东西,多看几遍就理解了,读书百遍其义自现嘛。即使不理解,当八股文背下来也没啥坏处。
内存过期回收算法
目前市面上,你能听到的过期回收算法基本就是两种,一个叫 LRU 一个叫 LFU 。当然,还有别的算法,不过我也不知道,不知为不知,咱们就先来学习知道的东西。
它们俩都是内存管理的一种页面置换算法。就是说,把没用的内存页转换出来,或者说是淘汰掉。假如说,如果我们没有这样的淘汰机制,那么内存里的数据一直存在,最后的结果是啥?当然就是内存被撑爆了。
一般情况下,我们会为缓存数据设置过期时间,但是,假如所有数据都没有设置过期时间呢?这样的数据一直放着撑爆内存是早晚的事嘛。所以说,我们就需要有一种过期回收的淘汰机制,避免内存无限制的增长最终导致服务完全不可用。
LRU:最近最少使用算法(Least Recently Used),意思就是直接淘汰最长时间没有被使用的内存页。
LFU:最不经常使用算法(Least Frequently Used),淘汰一段时间内的,使用次数最少的内存页。
在 Redis4.0 之前,Redis 只支持 LRU ,同时 LRU 也是 Memcached 的默认算法。但是,LRU 有一个很严重的问题,那就是一个 N 久不被使用的页,突然被使用了一下,它就变成最新的数据了。然后可能又是过 N 久才会使用到一次。而 LFU 则是在一定时间范围内对访问数据进行了限定。因此,如果是热点数据比较多,使用LFU 会更好一些。而热点不那么明显,或者热点数据总量并不多的话,使用 LRU 也是完全没问题的,并且这也是多数缓存系统所推荐的算法。
那么在 Redis 中如何来进行相关的配置呢?我们一个一个来看。
maxmemroy
这个配置之前其实我们也讲过,它是设置当前 Reids 实例最大可用内存限制。比如我们设置成 100MB 的话,那么当前这个实例中就只能存放 100MB 的数据。如果不设置它的话,就会一直向系统申请内存,也就是以整个机器和系统的可用内存为边界的。
通常情况下,我们会单独用一台机器专门部署 Redis ,所以这个值可设可不设。通过 CONFIG 相关的命令也可以进行运行时的配置。比如我本地就没有设置这个值
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "0"
maxmemory-policy
在之前的文章中,其实我们已经学过这个配置,当时是讲一个 LFU 相关的命令,不知道大家还记得不,文章在这里 Redis基础学习:通用命令(二)https://mp.weixin.qq.com/s/eiMRo_iJv0EjgGCBFTrfLg 。
当时我们是为了演示 OBJECT FREQ 命令查看到 LFU 算法的引用计数情况来修改的这个配置。它的实际意思就是当达到 maxmemory 设置的最大内存之后,使用何种方式进行内存的回收淘汰。具体可设置的值包括下面几种:
noeviction: 内存达到限制后,新添加进来的值不会被保存。如果是做了主从的数据库,则对主库适用。
allkeys-lru: 保留最近使用的,删除最近最少使用的。(LRU)
allkeys-lfu: 保留使用频率高的,删除使用频率低的。(LFU)
volatile-lru: 删除在过期集合中的最少使用的。(LRU)
volatile-lfu: 删除在过期集合中的使用频率低的。(LFU)
allkeys-random: 随机删除,为新添加的数据腾出空间。
volatile-random: 随机删除在过期集合中的。
volatile-ttl: 删除使用频率最低的,并在过期集合中的,并删除最短生存时间的。
啥叫过期集合?在我们 DEL 一条数据后,这条数据所在的内存页并不是马上就被回收淘汰的。这些数据会进入到一个过期集合中,或者说是相关的 KEY 会被设置为已经过期的。
在 Redis 中,EXPIRE 这类命令能关联到一个有额外内存开销的 KEY 。当 KEY 执行过期操作时,Redis 会确保按照规定时间删除它们。
上面带 volatile 的都是优先去处理这些过期集合中的数据,另外 Reids 也会在后台有一个线程专门去处理已经过期的 KEY ,但是,内存是页的,假设当前的 KEY 所在的页全部都过期了,那么这个内存页就会直接被回收,所以,如果这个内存页中还有其它的数据是没有过期的,那么这个内存页是不会被回收的。因此,如果你发现 Redis 占用的内存大于了设置的 maxmemory 时也不要惊讶。此外,如果所有的 KEY 都没有设置过期时间,那么这些配置和 noeviction 是没啥区别的。
怎么配置?
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
默认情况下,maxmemory-policy 走的是 noeviction 这个配置。也就是没有任何算法来处理,就是添加不了新数据了。具体要配置那种,还是要根据业务情况来定。
有热点数据,肯定是 allkeys-lfu ,当然 allkeys-lru 也没问题。数据小,碎片化多,用 allkeys-lfu ,大的文章之类的单个 KEY 的数据比较大,可以考虑 allkeys-lru 。
随机访问,或者循环访问,直接就 allkeys-random 好了。
大部分业务数据都有设置过期时间,那就 volatile-ttl 。
回收的工作过程
对于回收的操作,可以分几步来看:
客户端运行添加命令
Redis 检查内存使用情况,如果大于 maxmemory 的限制,则根据 maxmemory-policy 的策略进行淘汰
执行命令
也就是说,当内存到达 maxmemory 的边界时,我们会不断的在这个边界范围内来回跳转,超过它,然后回收后又返回到限制内。如果有某个命令导致大量的内存被使用,那么内存限制就会被明显的超过。
总结
很理论吧?还好内容也不多,一定要好好消化一下哦。在 Redis 中,LRU 和 LFU 也是采用的近似算法,有兴趣的同学可以通过下面的链接去官网进行更深入的研究。
参考文档:
https://redis.io/docs/manual/eviction/