Redis-过期删除策略和内存淘汰策略

简介、我们先来看如下几个问题:

①、如何设置Redis键的过期时间 ?

②、设置完一个键的过期时间后,到了这个时间,这个键还能获取到么?假如获取不到那这个键还占据着内存吗 ?

③、如何设置Redis的内存大小?当内存满了之后,Redis有哪些内存淘汰策略?我们又该如何选择 ?


一、设置Redis键过期时间


1、Redis提供了四个命令来设置过期时间(生存时间)


①、expire :表示将键 key 的生存时间设置为 ttl 秒。


②、pexpire :表示将键 key 的生存时间设置为 ttl 毫秒。


③、expireat :表示将键 key 的生存时间设置为 timestamp 所指定的秒数时间戳。


④、pexpireat :表示将键 key 的生存时间设置为 timestamp 所指定的毫秒数时间戳。


注释:在Redis内部实现中,前面三个设置过期时间的命令最后都会转换成最后一个PEXPIREAT 命令来完成。


返回键的剩余生存时间

  • TTL :以秒的单位返回键 key 的剩余生存时间。

  • PTTL :以毫秒的单位返回键 key 的剩余生存时间。


2、Redis过期时间的判定

在Redis内部,每当我们设置一个键的过期时间时,Redis就会将该键带上过期时间存放到一个过期字典中。当我们查询一个键时,Redis便首先检查该键是否存在过期字典中,

如果存在,那就获取其过期时间。然后将过期时间和当前系统时间进行比对,比系统时间大,那就没有过期;反之判定该键过期。


二、过期删除策略

删除策略就是针对已过期数据的处理策略,已过期的数据不一定被立即删除,在不同的场景下使用不同的删除方式会有不同效果,在内存占用与 CPU 占用之间寻找一种平衡,

顾此失彼都会造成整体 Redis 性能的下降,甚至引发服务器宕机或内存泄露


针对过期数据有三种删除策略:

  • 定时删除

  • 惰性删除(被动删除)

  • 定期删除


1、定时删除


在设置键的过期时间的同时,创建一个 定时器(timer),让定时器在键的过期时间到达时,立即执行对键的删除操作

  • 优点:节约内存,到时就删除,快速释放掉不必要的内存占用

  • 缺点:对 CPU 不友好,无论 CPU 此时负载多高均占用 CPU,会影响 Redis 服务器响应时间和指令吞吐量

  • 总结:用处理器性能换取存储空间(拿时间换空间)


创建一个定时器需要用到 Redis 服务器中的时间事件,而时间事件的实现方式是无序链表,查找一个事件的时间复杂度为 O(N),并不能高效地处理大量时间事件,所以采用这种方式并不现实


2、惰性删除


数据到达过期时间不做处理,等下次访问到该数据时执行 expireIfNeeded() 判断:

  • 如果输入键已经过期,那么 expireIfNeeded 函数将输入键从数据库中删除,接着访问就会返回空

  • 如果输入键未过期,那么 expireIfNeeded 函数不做动作


所有的 Redis 读写命令在执行前都会调用 expireIfNeeded 函数进行检查,该函数就像一个过滤器,在命令真正执行之前过滤掉过期键


惰性删除的特点:

  • 优点:节约 CPU 性能,删除的目标仅限于当前处理的键,不会在删除其他无关的过期键上花费任何 CPU 时间

  • 缺点:内存压力很大,出现长期占用内存的数据,如果过期键永远不被访问,这种情况相当于内存泄漏

  • 总结:用存储空间换取处理器性能(拿空间换时间)


3、定期删除

定期删除策略是每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响

  • 如果删除操作执行得太频繁,或者执行时间太长,就会退化成定时删除策略,将 CPU 时间过多地消耗在删除过期键上

  • 如果删除操作执行得太少,或者执行时间太短,定期删除策略又会和惰性删除策略一样,出现浪费内存的情况


定期删除是周期性轮询 Redis 库中的时效性数据,从过期字典中随机抽取一部分键检查,利用过期数据占比的方式控制删除频度

  • Redis 启动服务器初始化时,读取配置 server.hz 的值,默认为 10,执行指令 info server 可以查看,每秒钟执行 server.hz 次 serverCron() → activeExpireCycle()

  • activeExpireCycle() 对某个数据库中的每个 expires 进行检测,工作模式:

    • 轮询每个数据库,从数据库中取出一定数量的随机键进行检查,并删除其中的过期键,如果过期 key 的比例超过了 25%,则继续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒

    • 全局变量 current_db 用于记录 activeExpireCycle() 的检查进度(哪一个数据库),下一次调用时接着该进度处理

    • 随着函数的不断执行,服务器中的所有数据库都会被检查一遍,这时将 current_db 重置为 0,然后再次开始新一轮的检查


定期删除特点:

  • CPU 性能占用设置有峰值,检测频度可自定义设置

  • 内存压力不是很大,长期占用内存的冷数据会被持续清理

  • 周期性抽查存储空间(随机抽查,重点抽查)


三、内存淘汰策略


我们看到,通过过期删除策略,对于某些永远使用不到的键,并且多次定期删除也没选定到并删除,那么这些键同样会一直驻留在内存中,

又或者在Redis中存入了大量的键,这些操作可能会导致Redis内存不够用,这时候就需要Redis的内存淘汰策略了。


1、逐出算法

数据淘汰策略:当新数据进入 Redis 时,在执行每一个命令前,会调用 freeMemoryIfNeeded() 检测内存是否充足。如果内存不满足新加入数据的最低存储要求,

Redis 要临时删除一些数据为当前指令清理存储空间,清理数据的策略称为逐出算法


逐出数据的过程不是 100% 能够清理出足够的可使用的内存空间,如果不成功则反复执行,当对所有数据尝试完毕,如不能达到内存清理的要求,出现 Redis 内存打满异常:

(error) OOM command not allowed when used memory >'maxmemory'


2、策略配置

Redis 如果不设置最大内存大小或者设置最大内存大小为 0,在 64 位操作系统下不限制内存大小,在 32 位操作系统默认为 3GB 内存,一般推荐设置 Redis 内存为最大物理内存的四分之三


内存配置方式:

  • 通过修改文件配置(永久生效):修改配置文件 maxmemory 字段,单位为字节

  • 通过命令修改(重启失效):

    • config set maxmemory 104857600:设置 Redis 最大占用内存为 100MB

    • config get maxmemory:获取 Redis 最大占用内存

    • info :可以查看 Redis 内存使用情况,used_memory_human 字段表示实际已经占用的内存,maxmemory 表示最大占用内存


影响数据淘汰的相关配置如下,配置 conf 文件:

  • 每次选取待删除数据的个数,采用随机获取数据的方式作为待检测删除数据,防止全库扫描,导致严重的性能消耗,降低读写性能

    maxmemory-samples count
    
  • 达到最大内存后的,对被挑选出来的数据进行删除的策略

    maxmemory-policy policy
    


数据删除的策略 policy:3 类 8 种


第一类:检测设置过期时间的数据(可能会过期的数据集 server.db[i].expires):

  volatile-lru	# 对设置了过期时间的 key 选择最近最少使用的数据淘汰  最近最少使用的键
  volatile-lfu	# 对设置了过期时间的 key 选择最不常用数据淘汰
  volatile-ttl	# 对设置了过期时间的 key 选择将要过期的数据淘汰
  volatile-random	# 对设置了过期时间的 key 选择任意数据淘汰


第二类:检测全库数据(所有数据集 server.db[i].dict ):

  allkeys-lru		# 对所有 key 选择最近最少使用的数据淘汰
  allkeLyRs-lfu	    # 对所有 key 选择最不常用的数据淘汰
  allkeys-random	# 对所有 key 选择任意数据淘汰,相当于随机


第三类:放弃数据驱逐

  no-enviction	# 禁止驱逐数据(redis4.0中默认策略),会引发OOM(Out Of Memory)


在redis.conf 配置文件中,可以设置淘汰方式:

posted @ 2024-12-17 22:18  jock_javaEE  阅读(64)  评论(0)    收藏  举报