【Redis】缓存预热、雪崩、击穿、穿透、过期删除策略、内存淘汰策略

Redis常见问题总结:

Redis常见问题总结

Redis缓存预热

"宕机"服务器启动后迅速宕机,指的是服务刚上线由于Redis中没有任何数据,导致大量请求访问数据库,主从之间数据吞吐量较大,数据同步操作频度较高。

而缓存预热解决的就是,在系统启动前,提前将相关的数据直接加载到缓存系统。避免用户在请求的时候,先查询数据库,再将数据库的查询到的数据缓存到Redis的问题。用户可以直接查询事先被预热的缓存数据。

解决方案:

  • 准备工作:
    • 日常例行统计数据访问记录,统计访问频度较高的热点数据。
    • 将统计结果中的数据分类,根据级别,Redis优先加载级别较高的热点数据
  • 实施:
    • 使用脚本程序固定触发数据预热过程
    • 如果条件允许,使用CDN(内容分发网络),效果会更好

Redis缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。【这里指的是同一批key的过期时间相同或者Redis服务挂掉】

解决方案:

  • 给不同的Key的TTL添加随机值
  • 利用Redis集群提高服务的可用性
  • 给缓存业务添加降级限流策略
  • 给业务添加多级缓存

Redis缓存击穿

缓存击穿问题也叫 热点Key问题**,就是一个被高并发访问并且缓存重建业务较复杂的** key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击

常见的解决方案有两种:

  • 互斥锁
  • 逻辑过期

互斥锁:

逻辑过期:

Redis缓存穿透

缓存穿透是指客户端请求的 数据在缓存中和数据库中都不存在**,这样缓存永远不会生效,这些请求都会打到数据库**

常见两种解决方案:

  • 缓存空对象
  • 布隆过滤器

Redis 中 key 的过期删除策略

数据删除策略

什么是过期数据?

Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态

  • XX :具有时效性的数据
  • -1 :永久有效的数据
  • -2 :已经过期的数据或被删除的数据或未定义的数据

注意:过期的数据真的删除了吗?不是的

删除策略比对

  1. 定时删除 节约内存,无占用 不分时段占用CPU资源,频度高拿时间换空间

  2. 惰性删除 内存占用严重延时执行,CPU利用率高拿空间换时间

  3. 定期删除 内存定期随机清理每秒花费固定的CPU资源维护内存随机抽查,重点抽查

Redis的过期键的删除策略是指当Redis中的缓存的key过期了,Redis要如何处理。

Redis中提供了三种删除策略:

1.定时删除

当放入数据后,设置一个定时器,当定时器读秒完毕后,将对应的数据从dict中删除。

优点: 内存友好,数据一旦过期就会被删除

缺点: CPU不友好,定时器耗费CPU资源,并且频繁的执行清理操作也会耗费CPU资源。用时间换空间

2.惰性删除

当数据过期的时候,不做任何操作。当访问数据的时候,查看数据是否过期,如果过期返回null,并且将数据从内存中清除。如果没过期,就直接返回数据。

优点: CPU友好,数据等到过期并且被访问的时候,才会删除。

缺点: 内存不友好,会占用大量内存。用空间换时间

3.定期删除

定期删除是定时删除和惰性删除的折中方案。每隔一段时间对redisServer中的所有redisDb的expires依次进行随机抽取检查。

Redis中有一个server.hz定义了每秒钟执行定期删除的次数,每次执行的时间为250ms/server.hz。Redis中会维护一个current_db变量来标志当前检查的数据库。current_db++,当超过数据库的数量的时候,会重新从0开始。

定期检查就是执行一个循环,循环中的每轮操作会从current_db对应的数据库中随机依次取出w个key,查看其是否过期。如果过期就将其删除, 并且记录删除的key的个数。如果过期的key个数大于w25%,就会继续检查当前数据库,当过期的key小于w25%,会继续检查下一个数据库。当执行时间超过规定的最大执行时间的时候,会退出检查。一次检查中可以检查多个数据库,但是最多检查数量是redisServer中的数据库个数,也就是最多只能从当前位置检查一圈。

Redis内存淘汰策略

一、Redis对过期数据的处理

Redis的内存淘汰机制主要是用于在Redis用于缓存的内存不足时,处理需要新写入且需申请额外空间的数据。

(一)相关配置

  1. redis.conf中:
    • 配置maxmemory <bytes>,用于设置Redis的最大内存空间。若不设定该参数,默认无限制,通常会设定为物理内存的四分之三。
    • 配置maxmemory-policy noeviction,用于设置淘汰策略,默认为noeviction

(二)内存淘汰流程

  1. 客户端发起需要申请更多内存的命令(如set)。
  2. Redis检查内存使用情况,若已使用内存大于maxmemory,则根据用户配置的不同淘汰策略来淘汰内存(key),以换取一定的内存。
  3. 若上述步骤均无问题,则该命令执行成功。

(三)动态改配置命令

Redis支持动态改配置,无需重启。

  • 设置最大内存:config set maxmemory 100000
  • 设置淘汰策略:config set maxmemory-policy noeviction

二、内存淘汰策略的种类及特点

(一)Redis 4.0以前的策略

  1. noeviction:当内存使用超过配置时会返回错误,不会驱逐任何键。(默认选项,一般不选用)
  2. allkeys-lru:当内存不足以容纳新写入数据时,在整个键空间中,移除最近最少使用的key。(最常用)
  3. volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
  4. allkeys-random:当内存不足以容纳新写入数据时,在整个键空间中,随机移除某个key。
  5. volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
  6. volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。

(二)Redis 4.0以后增加的策略

  1. volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键。
  2. allkeys-lfu:从所有键中驱逐使用频率最少的键。

内存淘汰策略可通过配置文件修改,redis.conf对应的配置项是maxmemory-policy,修改对应的值即可。

三、常见的淘汰算法

(一)FIFO算法(先进先出)

  1. 思想
    • 基于队列的先进先出原则,最先进入的数据会被最先淘汰掉,是最简单、最公平的思想。
  2. 实现
    • 维护一个FIFO队列,按照时间顺序将各数据(已分配页面)链接起来组成队列,并将置换指针指向队列的队首。进行置换时,把置换指针所指的数据(页面)顺次换出,并把新加入的数据插到队尾即可。
  3. 缺点
    • 会导致缺页率增加。随着分配页面的增加,被置换的内存页面往往是被频繁访问的,因此FIFO算法会使一些页面频繁地被替换和重新申请内存,从而导致缺页率增加。由于缺页率会随着分配页面的增加而增加,使得redis的开销也逐渐增加,所以这种算法已不再使用。

(二)LRU算法(最近最少使用)

  1. 思想
    • 最近最少使用的会被优先淘汰。如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。当空间满时,最久没有访问的数据最先被淘汰掉。
  2. 实现
    • 用双向链表(LinkedList)+哈希表(HashMap)实现(链表用来表示位置,哈希表用来存储和查找),在Java里有对应的数据结构LinkedHashMap
  3. 缺点
    • 在需要淘汰时,只是随机选取有限的key进行对比,排除掉访问时间最久的元素,不能选择整个候选元素的最优解,只是局部最优。默认随机选取的key的数目为5,在配置文件redis.conf中由maxmemory_samples属性的值决定,采样数量越大越接近于标准LRU算法,但也会带来性能的消耗。
    • 在Redis 3.0以后增加了LRU淘汰池,进一步提高了与标准LRU算法效果的相似度。淘汰池即维护的一个数组,数组大小等于抽样数量maxmemory_samples,在每一次淘汰时,新随机抽取的key和淘汰池中的key进行合并,然后淘汰掉最旧的key,将剩余较旧的前面5个key放入淘汰池中待下一次循环使用。假如maxmemory_samples = 5,随机抽取5个元素,淘汰池中还有5个元素,相当于变相的maxmemory_samples = 10,所以进一步提高了与LRU算法的相似度。

(三)LFU算法(最不常使用)

  1. 思想
    • 如果一个数据在最近一段时间很少被访问到,那么在将来它被访问的可能性也很小。当空间满时,最小频率访问的数据最先被淘汰。
  2. 实现及问题解决
    • 为每个key维护一个计数器,每次key被访问时,计数器增大,计数器越大,则认为访问越频繁。但存在以下问题:
      • 可能存在某个key在开始一个小时内有100万的访问量,但之后一小时内访问量为0,而在第二个小时内另一个key的访问量达到20万,此时第二个小时内key1会优先于key2被淘汰,尽管key2在该小时内访问量更大。
      • 当新加入的key,由于没有被访问过,初始计数器为0,若此时触发淘汰机制,会把最先添加的key最先淘汰掉。
    • 解决方案:在LFU算法中维护了一个24bit的字段,被分成16 bits与8 bits两部分。第一部分(高16 bits)用来记录计数器的上次缩减时间,时间戳,单位精确到分钟。第二部分(低8 bits)用来记录计数器的当前数值,该数值反映访问频率,而非次数。
    • redis.conf配置文件中,lfu-log-factor用来调整计数器counter的增长速度,lfu-log-factor越大,counter增长越慢。lfu-decay-time是一个以分钟为单位的数值,用来调整counter的缩减速度。

(四)LRU和LFU的选择

需要根据业务权衡到底是选择淘汰最近最少使用(LRU)还是选择最不经常使用(LFU)。总的来说,无论是LRU、LFU、TTL还是Random都是近似算法来实现的,在可靠性和性能上做了一定的平衡。在业务中应主动删除没有价值的数据,或者更新某些key的过期时间等来提高Redis的性能和空间,不能过分依赖于淘汰策略。

(此处可根据需要插入相关图片,例如LRU和LFU算法的示意图等,假设插入了一张LRU算法的示意图,如下所示)

总结

Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时需要申请额外空间的数据;过期策略用于处理过期的缓存数据。要根据实际业务场景和需求合理选择内存淘汰策略,并结合主动的数据管理操作来优化Redis的性能和内存使用。

相关推荐
武子康3 分钟前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
Oak Zhang1 小时前
sharding-jdbc自定义分片算法,表对应关系存储在mysql中,缓存到redis或者本地
redis·mysql·缓存
门牙咬脆骨2 小时前
【Redis】redis缓存击穿,缓存雪崩,缓存穿透
数据库·redis·缓存
门牙咬脆骨2 小时前
【Redis】GEO数据结构
数据库·redis·缓存
WindFutrue3 小时前
使用Mybatis向Mysql中的插入Point类型的数据全方位解析
数据库·mysql·mybatis
AiFlutter4 小时前
Java实现简单的搜索引擎
java·搜索引擎·mybatis
墨鸦_Cormorant4 小时前
使用docker快速部署Nginx、Redis、MySQL、Tomcat以及制作镜像
redis·nginx·docker
Dlwyz7 小时前
问题: redis-高并发场景下如何保证缓存数据与数据库的最终一致性
数据库·redis·缓存
天天扭码7 小时前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
飞升不如收破烂~8 小时前
redis的List底层数据结构 分别什么时候使用双向链表(Doubly Linked List)和压缩列表(ZipList)
redis