Redis内存淘汰策略

一是内存爆了要用 LRU、LFU、FIFO 清理,否则磁盘SWAP,性能急剧下降

二是超时键过期要删除,用主动或惰性的方法

通用过期策略

  • 定时过期:

    每个设置过expire时间的key都要创建一个定时器,到期立即清除

    该策略可以立即清除过期的数据,对内存很友好

    但占用大量CPU资源处理过期数据,从而影响响应时间和吞吐量

  • 惰性过期:

    访问key才会判断该key是否已过期,过期则清除

    该策略可最大化节省CPU资源,却对内存非常不友好

    极端情况可能出现大量过期key没有再次被访问,而不会被清除,占用大量内存

  • 定期过期:

    每隔一定的时间,扫描一定数量expires字典中一定数量的key,并清除其中已过期的key

    该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可在不同情况下使CPU和内存资源达到最优的平衡效果

    (expires字典,key指向键的指针,value是该键的毫秒精度UNIX时间戳)

Redis同时使用惰性过期和定期过期两种

Redis 数据淘汰策略

  • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用 的数据淘汰

  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数 据淘汰

  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据 淘汰

  • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

  • no-enviction(驱逐):禁止驱逐数据

实现逻辑

redis

2.6 随机找三条记录出来,比较哪条空闲时间最长就删哪条,再随机三条出来,一直删到内存足够

3.0 改进:每次随机五条记录出来,插入到一个长度为十六的按空闲时间排序的队列里,排头的那条删掉,再找五条出来,继续尝试插入队列,不只在一次随机的五条里面找最久那条,会连同之前的一起做比较……

Memcached

中规中矩的,用了教科书式的双向链表存储 slab 内的 LRU 关系

元素插入时把自己放到列头,删除时把自己的前后两个元素对接起来,更新时先做删除再做插入

分配内存超限时,从 LRU 的队尾开始清理

Guava Cache

同 Memcached

Ehcache

随机 8 条记录,找出最旧那条,刷到磁盘里

redis 默认设置都是 noeviction,内存不够直接报错,懒得建个双向链表,而且每次访问时都要更新它


redis

主线程 100 毫秒执行一次,随机抽 20 条 Key ,1/4 过期,证明此时过期的键可能比较多,就不等 100 毫秒,立刻开始下一轮的检查

限定每轮的总执行时间不超过 1 毫秒优化CPU占用

Memcached

LRU 爬虫线程,利用之前LRU 队列,可设置多久跑一次 (默认也是 100 毫秒),沿着列尾一直检查过去,每次检查 N 条数据。但没有 Redis 过期的多了立马展开下一轮检查的功能,需要自己自己权衡 N 的设置

Guava Cache

检查调用时机并不是 100 毫秒什么的,而是每次各种写入数据时的 preWriteCleanup()

Ehcache

只有惰性检查,没有主动检查过期的,只会在内存超限时不断用近似 LRU 算法把内存中的元素刷到磁盘中

文件存储中才有超时检查的线程


适用场景

  • allkeys-lru

    一部分访问频率较高,其余部分访问频率较低

    或无法预测数据使用频率

  • allkeys-random

    数据访问概率大致相等

    如果研发者需要通过设置不同的ttl来判断数据过期的先后顺序,此时可以选择volatile-ttl策略。

  • volatile-lru或volatile-random

    希望一些数据能长期被保存,而一些数据可以被淘汰掉

  • allkeys-lru

    设置expire会消耗额外内存,allkeys-lru就可以不再设置过期时间,高效利用内存


lru淘汰根据 maxmemory-samples