Redis系列:使用Redis实现缓存及相关问题

2022-12-01 21:54:15 浏览数 (1)

Redis 会把 MySQL 中经常被查询的数据缓存起来,比如热点数据,这样当用户来访问的时候,就不需要到 MySQL 中去查询了,而是直接获取 Redis 中的缓存数据,从而降低了后端数据库的读取压力。如果说用户查询的数据 Redis 没有,此时用户的查询请求就会转到 MySQL 数据库,当 MySQL 将数据返回给客户端时,同时会将数据缓存到 Redis 中,这样用户再次读取时,就可以直接从 Redis 中获取数据。 本篇内容包括:关于 Redis 缓存,缓存相关问题(包括 Redis 缓存热 key 问题、Redis 缓存穿透问题、关于布隆过滤器、Redis 缓存击穿问题 与 Redis 缓存雪崩问题的相关内容),Redis 缓存预热,Redis 缓存过期与内存淘汰策略。


文章目录
  • 一、关于 Redis 缓存
    • 1、Redis 如何实现缓存
    • 2、Redis 缓存更新策略

  • 二、缓存相关问题
    • 1、Redis 缓存热 key 问题
    • 2、Redis 缓存穿透问题
    • 3、关于布隆过滤器
    • 4、Redis 缓存击穿问题
    • 5、Redis 缓存雪崩问题
  • 三、Redis 缓存预热
  • 四、Redis 缓存过期与内存淘汰策略
    • 1、Redis 的缓存过期策略
    • 2、Redis 的内存淘汰策略

一、关于 Redis 缓存

1、Redis 如何实现缓存

在请求达到后端之后,对需要进行缓存的接口,会先去 Redis 中找有无数据,没有的话会继续走正常的业务流程,然后将查询到的结果返回给客户端的同时也放在 Redis 中一份,下次相同请求进来后,就可以直接从 Redis中 拿到数据。

在进行缓存之后,相同的请求在缓存时间内是不会去读取数据库的,但是此时如果修改了数据库,则接口返回的数据就不能保证和数据库一致,因此在增、删、改时我们需要刷新缓存。

2、Redis 缓存更新策略

缓存更新的策略有很多,这里比较两种情况

  • 第一种情况,先更新数据库再同步更新缓存或者先更新缓存再同步更新数据库,其实都属于 write through,同步更新的好处在于可以很好的保持数据的一致性,但是缺点在于同步更新时,必然会影响性能;
  • 第二种情况,先更新缓存,然后再异步写回数据库,也就是 write back,异步写回的好处在于不会影响缓存的高性能,能够快速响应客户端,但是缺点在于在数据异步写回到数据库之前,存在缓存和数据库数据短暂不一致。

二、缓存相关问题

1、Redis 缓存热 key 问题

所谓热 key 问题就是,突然有几十万的请求去访问 Redis 上的某个特定 key,那么这样会造成流量过于集中,达到物理网卡上限,从而导致这台 Redis 的服务器宕机引发雪崩。

针对热 key 的解决方案:

  1. 提前把热 key 打散到不同的服务器,降低压力
  2. 二级缓存,提前加载热 key 数据到内存中,如果 redis 宕机,走内存查询
2、Redis 缓存穿透问题

访问一个缓存和数据库都不存在的 key,此时会直接打到数据库上,并且查不到数据,没法写缓存,所以下一次同样会打到数据库上。此时,缓存起不到作用,请求每次都会走到数据库,流量大时数据库可能会被打挂。此时缓存就好像被“穿透”了一样,起不到任何作用。

缓存穿透解决方案:

  1. 接口校验:在正常业务流程中可能会存在少量访问不存在 key 的情况,但是一般不会出现大量的情况,所以这种场景最大的可能性是遭受了非法攻击。可以在最外层先做一层校验:用户鉴权、数据合法性校验等,例如商品查询中,商品的 ID 是正整数,则可以直接对非正整数直接过滤等等。
  2. 缓存空值:当访问缓存和 DB 都没有查询到值时,可以将空值写进缓存,但是设置较短的过期时间,该时间需要根据产品业务特性来设置。
  3. 布隆过滤器:使用布隆过滤器存储所有可能访问的 key,不存在的 key 直接被过滤,存在的 key 则再进一步查询缓存和数据库。
3、关于布隆过滤器

布隆过滤器的特点是判断不存在的,则一定不存在;判断存在的,大概率存在,但也有小概率不存在。并且这个概率是可控的,我们可以让这个概率变小或者变高,取决于用户本身的需求。

布隆过滤器由一个 bitSet 和 一组 Hash 函数(算法)组成,是一种空间效率极高的概率型算法和数据结构,主要用来判断一个元素是否在集合中存在。

HashMap 和 布隆过滤器:其实当数据量不大时,HashMap 实现起来一点问题都没有,而且还没有误判率。不过,当数据量上去后,布隆过滤器的空间优势就会开始体现,特别是要存储的 key 占用空间越大,布隆过滤器的优势越明显。

4、Redis 缓存击穿问题

某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

缓存击穿解决方案:

  1. 加互斥锁:在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。因为这个可以保证只有一个请求会走到数据库;
  2. 热点数据不过期:直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。这种方式适用于比较极端的场景,例如流量特别特别大的场景,使用时需要考虑业务能接受数据不一致的时间,还有就是异常情况的处理,不要到时候缓存刷新不上,一直是脏数据,那就凉了。
5、Redis 缓存雪崩问题

大量的热点 key 设置了相同的过期时间,导在缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库被打挂。缓存雪崩其实有点像“升级版的缓存击穿”,缓存击穿是一个热点 key,缓存雪崩是一组热点 key。

缓存雪崩解决方案

  1. 过期时间打散。既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效;
  2. 加互斥锁。该方式和缓存击穿一样,按 key 维度加锁,对于同一个 key,只允许一个线程去计算,其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可;
  3. 热点数据不过期。该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的情况。

三、Redis 缓存预热

缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

  • 请求数量较高
  • 主从之间数据吞吐量较大,数据同步操作频度较高

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

缓存预热解决方案:

  1. 日常例行统计数据访问记录,统计访问频度较高的热点数据
  2. 利用 LRU 数据删除策略, 构建数据留存队列例如:Storm 与 Kafka 配合
  3. 将统计结果中的数据分类, 根据级别, Redis 优先加载级别较高的热点数据
  4. 利用分布式多服务器同时进行数据读取,提速数据加载过程

缓存预热实施:

  1. 使用脚本程序固定触发数据预热过程
  2. 如果条件允许, 使用了 CDN(内容分发网络), 效果会更好。

四、Redis 缓存过期与内存淘汰策略

1、Redis 的缓存过期策略

Redis 主要有 2 种过期删除策略:

  • 惰性删除指的是当我们查询 key 的时候才对 key 进行检测,如果已经达到过期时间,则删除。显然,他有一个缺点就是如果这些过期的 key 没有被访问,那么他就一直无法被删除,而且一直占用内存;
  • 定期删除指的是 Redis 每隔一段时间对数据库做一次检查,删除里面的过期 key。由于不可能对所有 key 去做轮询来删除,所以 Redis 会每次随机取一些 key 去做检查和删除。
2、Redis 的内存淘汰策略

假设 Redis 每次定期随机查询 key 的时候没有删掉,这些 key 也没有做查询的话,就会导致这些 key 一直保存在 Redis 里面无法被删除,这时候就会走到 Redis 的内存淘汰策略。

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

  1. volatile-lru:从已设置过期时间的 key 中,移除最近最少使用的key进行淘汰
  2. volatile-ttl:从已设置过期时间的 key 中,移除将要过期的 key
  3. volatile-random:从已设置过期时间的 key 中随机选择 key 淘汰
  4. allkeys-lru:从 key 中选择最近最少使用的进行淘汰
  5. allkeys-random:从 key 中随机选择 key 进行淘汰
  6. noeviction:当内存达到阈值的时候,新写入操作报错

Redis 的内存淘汰策略的选取并不会影响过期的 key 的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据。

0 人点赞