性能文章>【全网首发】Redis系列11:内存淘汰策略>

【全网首发】Redis系列11:内存淘汰策略原创

2周前
208546

Redis系列1:深刻理解高性能Redis的本质
Redis系列2:数据持久化提高可用性
Redis系列3:高可用之主从架构
Redis系列4:高可用之Sentinel(哨兵模式)
Redis系列5:深入分析Cluster 集群模式
追求性能极致:Redis6.0的多线程模型
追求性能极致:客户端缓存带来的革命
Redis系列8:Bitmap实现亿万级数据计算
Redis系列9:Geo 类型赋能亿级地图位置计算
Redis系列10:HyperLogLog实现海量数据基数统计

1 前言

通过前面的一些文章我们知道,Redis的各项能力是基于内存实现的,相对其他的持久化存储(如MySQL、File等,数据持久化在磁盘上),性能会高很多,这也是高速缓存的一个优势。
但是问题来了,每一台机器内存终归是有限的,即使是集群模式,总的内存空间也是有限的,不能无限制的消耗。而在Redis的使用过程中,很有可能出现使用消耗超过内存实际大小的情况。比如以下几种情况:

  • 未设置过期时间,Redis的Key将一直存在,直至我们明确将它删除。
  • 过度跟不合理的持久化(无论是RDB快照 或是 AOF日志),都会在内存和磁盘中反复操作,需要一定的内存空间进行处理。
  • 不及时清理过期缓存:清理过期缓存的方式主要有以下两种,并不是实时或者准实时,所以存在部分过期缓存依旧存在的问题。
    • 主动定期删除: Redis 默认每 1 秒运行 10 次(平均每 100 ms 执行一次),每次随机抽取部分设置过期时间的 key,检查是否过期,若是过期就直接删除,直至过期的 key 比率低于 1/4。
    • 被动惰性删除:缓存过期并不马上清理,当客户端的请求查询该 key 的时候,检查下 key 是否过期,如果过期,则删除该 key,重新获取。如果长时间未请求,就会有过期缓存滞留。
  • 不合理不规范的使用缓存,导致内存耗尽,比如:
    • 过度使用缓存,既缓存冷数据也能缓存热数据,导致内存占用过多,性能也没有得到有效提高
    • 缓存数量过多或者单个缓存的Value体积过大
    • 缓存过期时间设置过长或者根本不设置

2 Redis内存淘汰策略

所以,如果放任上面的那几种情况,内存终归会满的,Redis自身有一套比较完善的内存淘汰策略来专门应对这个问题,在Redis Memory占用超过我们配置的阈值的时候触发策略执行。

# redis.conf 配置最大内存空间占用为2gb,超过则执行内存淘汰策略
redis > CONFIG SET maxmemory 2gb

内存淘汰策略一共有8中,除了一种不执行淘汰策略之外,其他7种都是按照各自不一的算法对内存中现有的数据进行处理。
我们下面详细来看一下这些淘汰策略,把他们分成三大类,8小类来逐一讲解:

2.1 不淘汰策略

2.1.1 noeviction 不淘汰策略

noeviction指的是即使资源超过 maxmemory 限制的值也不会执行淘汰,只是不允许创建新的缓存了。
当Redis内存占用达到我们上面的配置的阈值(比如 5gb)之后,就不允许新增缓存key了,当有新的缓存要创建的时候,Redis 直接返回error。

2.2 仅淘汰配置过期时间key

这边仅针对配置了过期时间的数据进行淘汰

2.3.1 volatile-lru :删除最近最少使用的key

LRU(Least Recently Used)是按照最近最少使用原则来筛选数据,即最不常用的数据会被筛选出来。
如果我们的服务中有冷热数据隔离需求,这无疑是一个比较好的办法。可以将缓存的一些不经常使用的冷数据,而且数据size比较大的,筛选出来清理掉。而近期频繁被使用的key就被保留下来了。
常见的场景如下:

  • 电商平台的冷热数据:比如冬季,保暖冬装、电暖设备的浏览次数就会升高,而相应的冷饮、制冷设备(冰箱、空调)的浏览次数就会降低,那么LRU策略下优先删除的就是最近一段时间未访问的缓存信息。
  • 外卖平台:每天的11~13点,17~19点,一定是美食外卖品种的高频率访问时间段,而日用品、果蔬生鲜 大都会避开这个高峰期,这时如果内存不够用了,那么就会成为被优先删除的缓存类型。

image.png

2.3.2 volatile-lfu:删除访问次数最少的key(4.0 之后新增的策略)

LRU算法的不足之处在于,一个本身很少被访问的key,只是刚刚被访问了1次,就被认为是最近有使用的热点数据,导致短时间内不会被淘汰。
而LFU弥补了这个不足,LFU(Least Frequently Used)淘汰策略会根据key的最近访问频率进行淘汰,解决上面说的这个不足。

  • LFU在LRU的基础上,为每个数据增加了一个计数器,用于统计该数据的访问次数。
  • 当使用LFU策略淘汰数据时,会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出内存。
  • 如果两个缓存数据的访问次数相同,LFU再比较这两个key最近一次的访问时间,把访问时间更早的缓存key淘汰出内存。

常见的应用场景:

  • 对于电商平台中的冷门的商品,电子书App中热度较低、阅读量较低的书籍。这种类型的缓存会优先被淘汰掉。

image.png

2.3.3 volatile-random:随机删除过期key

针对有配置过期时间,但没有明显的冷热访问频率区别,所有的查询分布比较均衡的数据。这时候就使用 allkeys-random 策略吧,让它随机选择需要淘汰数据,也相对公平。
常见的使用场景有:

  • 电商平台:常规时段的商品浏览。
  • 钉钉之类工具:老师无差别抽查学生的作业。

2.3.4 volatile-ttl:删除过期时间内剩余时间最短的key

这个特性仅限于配置过期时间的场景,它是根据当前时间 跟 过期时间的差额进行由短到长的排序,较短的优先淘汰。

 asc_sort(validate_time - current_time)

这种算法相对来说也不考虑缓存的访问频率和重要程度,仅按照创建的先后进行清理,越早的缓存越早清理。
所以不具备明显特征的业务场景都适用。

2.3.5 补充说明

业务场景有一些数据始终不需要删除,比如置顶新闻、视频,还有我们自己置顶的weibo。为了保障它们不被清理掉,就给这些数据不设置过期时间,这样的话 volatile类型的淘汰策略就不会影响了。但如果是 allkeys 开头的策略依旧会影响到。

2.3 淘汰所有缓存类型的key

无论是否配置了过期时间的数据均可进行淘汰。
从微服务拆分的角度说,不同的服务类型个方向的服务进行院子隔离会比较一点。这一点设计思维在缓存上依旧适用。
我们可以将不需要过期时间的缓存信息 和 需强制配置过期时间的缓存key分开。针对业务场景分别使用 volatile-xxx 策略 和 allkyes-xxx策略。

2.3.1 allkeys-lru:删除最近最少使用的key

保留最近有使用的key,类似volatile-lru

2.3.2 allkeys-lfu:删除访问次数最少的key

最不经常使用的,类似volatile-lfu

2.3.3 allkeys-random:随机删除过期key

无差别随机删除,volatile-random,为添加新数据腾出空间

2.4 策略命令的使用

# 获取当前内存淘汰策略
redis > config get maxmemory-policy

# 获取Redis能使用的最大内存大小:如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存。
redis > config get maxmemory

#  通过命令配置淘汰策略
redis > config set maxmemory-policy volatile-lru

# 设置Redis最大占用内存大小,这边最大占用内存大小配置为2000M
redis > config set maxmemory 2000mb

3 总结

一张图总结
image.png
欢迎关注公众号,bat一线资深研发、架构师,专注技术!
image.png

点赞收藏
分类:标签:
Brand

搜索关注微信公众号【架构与思维】:撰稿者为bat、字节的几位高阶研发/架构,专注技术分享。

请先登录,查看4条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

【全网首发】追求性能极致:Redis6.0的多线程模型

【全网首发】追求性能极致:Redis6.0的多线程模型

【全网首发】追求性能的极致:Redis6.0的客户端缓存

【全网首发】追求性能的极致:Redis6.0的客户端缓存

Redis源码简洁剖析14 —AOF

Redis源码简洁剖析14 —AOF

【全网首发】Redis系列8:Bitmap实现亿万级数据计算

【全网首发】Redis系列8:Bitmap实现亿万级数据计算

【译】如何使用MySQL来设计分布式锁?

【译】如何使用MySQL来设计分布式锁?

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

6
4