Redis的淘汰策略你知多少

177 阅读4分钟

本文正在参加「技术专题19期 漫谈数据库技术」活动

前言

后端用Redis做缓存的场景很常见,而Redis的淘汰策略也有很多。那么你知道该如何选择淘汰策略吗?本文给你答案。

Redis有哪些淘汰策略

首先,我们要设置Redis的缓存大小,命令:CONFIG SET maxmemory 4gb,或者在redis.conf配置文件直接设置:maxmemory 4gb。 建议将缓存容量设置为总数据量的15%-30%,平衡访问性能和内存空间开销。 redis的淘汰策略有以下几种:

淘汰策略策略释义
noeviction不会数据淘汰:缓存满了不再接收写请求,返回错误
volatile-random对设置了过期时间的数据随机删除
volatile-ttl根据过期时间的先后删除设置了过期时间的
volatile-lru使用LRU算法筛选设置了过期时间的数据然后删除
volatile-lfu(Redis 4.0 后)使用LFU算法筛选设置了过期时间的数据然后删除
allkeys-lru使用LRU算法对所有数据筛选后删除
allkeys-random随机对所有数据筛选后删除
allkeys-lfu(Redis4.0后)使用LFU算法对所有数据筛选后删除

LRU算法

LRU算法,就是筛选最近最少使用的数据,优先淘汰这些数据。核心思想就是“若数据最近被访问过,将来被访问的几率也会更高”。

LRU的底层是hash表+双向链表。hash表保证了查询的时间复杂度是O(1),而双向链表则保证了节点插入及删除的时间复杂度也为O(1)。

结合下图,我们就能知道LRU是如何筛选数据的了。

  1. LRU GET操作:当访问数据时,若数据节点存在,LRU会将该数据节点移动到链表的头部,并返回节点的值;
  2. LRU PUT操作:当节点不存在时,会新增节点,并将节点放在链表的头部;若节点存在,这时会更新节点,并将节点移动到链表头部。

仔细看的话,可以发现LRU算法有缺陷:

  • 它用链表来管理所有的缓存数据,数据量大的时候会带来很多空间开销。
  • 数据在访问时,LRU需要将数据移动到链表头部。大量数据的链表移动会很耗时,在Redis服务器中自然会降低Redis的性能。

因此,Redis权衡了这些影响,没有照搬LRU算法来淘汰数据,而是简化了LRU算法运用之。

Redis的近似LRU算法

Redis的做法是,键值对数据结构RedisObject会有lru字段,它记录了每个数据的最近一次被访问的Redis时钟(可理解成时间戳)。然后在Redis决定要淘汰数据时,它的策略是这样的:

  • 首次淘汰:随机取出最多N个数据放入待淘汰数据集合,源码里叫做evictionPoolEntry;
  • 再次淘汰:再随机取出最多N个数据,只要数据的lru比待淘汰数据集合的任一数据的lru小,就将数据填充至待淘汰数据集合;
  • 执行淘汰:取出待淘汰数据集合中lru最小的一条数据淘汰掉。

在Redis中我们可通过配置maxmemory-samples参数来控制上面描述的集合数据个数N。比如设置500个数据作为待淘汰数据集合,CONFIG SET maxmemory-samples 500

从这种做法可以看出,Redis不需为所有数据维护一个链表,也不要每次有数据访问都去移动链表了,性能也就保证了下来。

小结

redis有8种淘汰策略,在我们选择Redis的淘汰策略时,可以参考下面方法:

  • 无特殊需求,优先考虑allkeys-lru策略。这样可以将最近访问的数据留在缓存中,提升访问性能;
  • 没有冷热数据区分,可使用allkeys-random策略。
  • 若需求中有置顶的要求,可考虑volatile-lru策略。对置顶的数据不设置过期时间,这样就能保证不被淘汰掉。