Redis的内存淘汰机制、过期策略

原文链接: https://blog.csdn.net/github_35124642/article/details/80576577

一、内存淘汰机制

        Redis作为当前最常用的开源内存数据库,性能十分高,据官方数据表示Redis读的速度是110000次/s,写的速度是81000次/s 。而且Redis支持数据持久化,众多数据结构存储,master-slave模式数据备份等多种功能。

        但是长期将Redis作为缓存使用,难免会遇到内存空间存储瓶颈,当Redis内存超出物理内存限制时,内存数据就会与磁盘产生频繁交换,使Redis性能急剧下降。此时如何淘汰无用数据释放空间,存储新数据就变得尤为重要了。

        对此,Redis在生产环境中,采用配置参数maxmemory 的方式来限制内存大小。当实际存储内存超出maxmemory 参数值时,开发者们可以通过这几种方法——Redis内存淘汰策略,来决定如何腾出新空间继续支持读写工作。

        Redis 内存淘汰指的是用户存储的一些键被可以被 Redis 主动地从实例中删除。内存的淘汰机制的初衷是为了更好地使用内存,用一定的缓存miss来换取内存的使用效率。

    淘汰机制和 Redis 最大占用内存 maxmemory 值以及过期时间 expireTime 紧密相连,典型的例子是:

  1.  客户端发起了需要申请更多内存的命令(如set)。
  2.  Redis 检查内存使用情况,如果已使用的内存大于 maxmemory 则开始根据用户配置的不同淘汰策略来淘汰已存储的数据(key),从而换取一定的内存。
  3. 如果上面都没问题,则这个命令执行成功。

        注:maxmemory 为 0 的时候表示我们对 Redis 的内存使用没有限制,redis 默认设置 maxmemory 为 0。

LRU淘汰

        LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

        在服务器配置中保存了 lru 计数器 server.lrulock,会定时(redis 定时程序 serverCorn())更新,server.lrulock 的值是根据 server.unixtime 计算出来进行排序的,然后选择最近使用时间最久的数据进行删除。另外,从 struct redisObject 中可以发现,每一个 redis 对象都会设置相应的 lru。每一次访问数据,会更新对应redisObject.lru。

        Redis中使用的是一种LRU近似算法:为实现近似的LRU算法,Redis给每个key增加了一个额外的字段,长度为24bit,也就是最后一次访问的时间。这里,LRU淘汰只有惰性处理,当Redis写操作时,内存超过了maxmemory,就会进行一次LRU淘汰算法。默认情况下,Redis会随机挑选5个键,并从中选择一个最久未使用的key进行淘汰。

        在配置文件中,按maxmemory-samples选项进行配置,选项配置越大,消耗时间就越长,但结构也就越精准。

TTL淘汰

        Redis 数据集数据结构中保存了键值对过期时间的表,即 redisDb.expires。与 LRU 数据淘汰机制类似,TTL 数据淘汰机制中会先从过期时间的表中随机挑选几个键值对,取出其中 ttl 最大的键值对淘汰。同样,TTL淘汰策略并不是面向所有过期时间的表中最快过期的键值对,而只是随机挑选的几个键值对。

随机淘汰

        在随机淘汰的场景下获取待删除的键值对,随机找hash桶再次hash指定位置的dictEntry即可。

        Redis中的淘汰机制都是几近于算法实现的,主要从性能和可靠性上做平衡,所以并不是完全可靠,所以开发者们在充分了解Redis淘汰策略之后还应在平时多主动设置或更新key的expire时间,主动删除没有价值的数据,提升Redis整体性能和空间。

Redis提供的淘汰策略:

  • 1. volatile-lru:从设置过期时间的数据集(server.db[i].expires)中挑选出最近最少使用的数据淘汰。没有设置过期时间的key不会被淘汰,这样就可以在增加内存空间的同时保证需要持久化的数据不会丢失。
  • 2. volatile-ttl:除了淘汰机制采用LRU,策略基本上与volatile-lru相似,从设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰,ttl值越小越优先被淘汰。
  • 3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。当内存达到限制无法写入非过期时间的数据集时,可以通过该淘汰策略在主键空间中随机移除某个key。
  • 4. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰,该策略要淘汰的key面向的是全体key集合,而非过期的key集合。
  • 5. allkeys-random:从数据集(server.db[i].dict)中选择任意数据淘汰。
  • 6. no-enviction:禁止驱逐数据,也就是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失,这也是系统默认的一种淘汰策略

二、常见的过期删除策略

Redis中带有过期时间的kry是保存在一个字典中的,以后会定时遍历这个字典,删除过期的key。

惰性删除:每次从键空间获取键时,都检查是否过期,过期则删除,未过期,则返回该键

       只有取出键时才对其进行过期检查,不会删除其他键,不会花费CPU太多时间。若一个键早已过期,但一直未被取到,它便会一直占用内存。若有大量的未被访问的键,而服务器又不会主动释放,就会造成大量的内存浪费。

定期删除:每隔一段时间删除里面的过期键

Redis默认会1秒进行10次过期扫描,过期扫描不会遍历字典中所有的kry,而是采用一种贪心的策略。

  1. 从字典中随机选出20个key。
  2. 删除这20个key中过期的key。
  3. 如果过期的key比例超过1/4,那就在进行步骤1.
  4. 同时,为了保证扫描不会出现循环过度,导致线程卡死的现象,算法会设置扫描时间的上限,默认为25ms。

redis中常见的过期键设置命令

  •     SETEX命令可以在设置一个字符串的同时为键设置过期时间。
  •     TTL和PTTL命令接受一个带生存时间或者过期时间的键,返回这个键的剩余时间。
  •     EXPIRE 将key生存时间设置为ttl秒
  •     PEXPIRE 将key生存时间设置为ttl毫秒
  •     EXPIREAT 将键key的过期时间设置为timestamp所指定的秒数时间戳
  •     PEXPIREAT 将键key的过期时间设置为timestamp所指定的豪秒数时间戳

Redis的内存淘汰机制、过期策略_第1张图片

redis惰性删除策略

Redis的内存淘汰机制、过期策略_第2张图片

redis定期删除策略

过期键的定期删除策略由redis.c/activeExpireCycle函数实现

  •     函数每次运行时,都从一定数量的数据库中取出一定数量的随机键检查,并删除过期键
  •     全局变量current_db会记录当前函数的检查进度,在下一次调用时,接着上次的进度处理。当前函数遍历10号数据库返回了,下次就会从11号开始
  •     随着函数的执行,所有数据库都被检查一遍后,全局变量current_db会置为0,然后重新开始

复制功能下如何处理

复制模式下,过期键的删除由主服务器控制

  •     主服务删除一个过期键后,会显示的向从服务器发送DEL命令,告知删除该键
  •     从服务器只有接收到主服务器发送来的DEL命令,才会删除过期键。否则不会删除,并且像正常键一样处理

这样可以保持数据的一致性

RDB和AOF时如何处理过期键

RDB

  •     以主服务器模式运行,在载入RDB文件时,未过期的键才会被载入,过期键被忽略
  •     以从服务器模式运行,所有键都将载入

AOF写入

  •     若某键已过期,程序会向AOF文件追加一条DEL,来显示删除

AOF重写

  •     已过期的键不会保存到重写的AOF文件中

 

你可能感兴趣的:(【NoSql】)