Redis 系列--缓存问题(缓存更新以及缓存穿透/缓存击穿/缓存雪崩)

39 篇文章 0 订阅
21 篇文章 0 订阅

使用 Redis 作为缓存时,一般的设计方案是:用户发起查询请求时,先从缓存中进行查询,如果数据存在则直接返回,否则查询数据库后更新缓存并返回。
在这里插入图片描述
在讨论缓存的常见问题之前,我们先来探讨一下缓存如何更新:
考虑一个场景,数据库有一条数据,同时也存在缓存中,现在我们要对这条数据进行更新,该如何进行更新呢?

首先考虑采用更新的操作(set)来实现缓存的更新,采用这种方案的话,不管是先更新缓存还是先更新数据库,都会存在数据一致性的问题。因此实际应用中我们一般不采用更新命令(set)来更新缓存,而是选择先删除(del)再插入(set)的方式进行缓存更新,那么同样的,我们该如何选择更新的顺序呢,是先更新数据库,还是先删除?

如果选择先更新数据库,如果数据库更新成功,删除操作失败,会导致数据不一致。如果选择先删除缓存,在删除之后,如果用户查询请求到达,查缓存不存在,会查询数据库,也不会出现数据一致性问题。好像这种方案没什么问题,但是考虑这个场景,A 客户端进行缓存更新操作,它的处理逻辑是这样的:1,先把 key 删除了;2,然后去更新数据库;3,更新完了将新的 value 写入 key。如果在步骤 1 完成之后 B 对同一条数据的查询请求进来了,发现缓存中查不到这条数据,这时它的处理逻辑是这样的:1,查询缓存失败去数据库查询;2,从数据库查询到数据,将数据同步到缓存并返回。观察 A 和 B 的处理逻辑会发现,如果 A 的 3 发生时间晚于 B 的 2,最终缓存中的数据和数据库中的数据是一致的,而且就算 B 查询到的是旧的数据(A 提交之前的数据)也没多大影响(B 看到的数据是一致的,而且出现 B 这种情况的请求也是有限的)。如果 A 的 3 早于 B 的 2 发生,就会出现数据不一致,A 更新的缓存的最新值被 B 用旧的值覆盖了。这种问题该如何解决呢?一般我们会选择在 A 更新数据库完成之后,延迟一小段时间再执行一次删除操作,让后面进来的第一个查询线程去将数据库中的最新值同步到缓存,之所以要延迟一小段时间再删除,就是为了避免删除的过早,又被另一个线程覆盖上旧的值。这种方案就是我们常说的延时双删方案。

现在我们再来讨论一下缓存查询的问题。上图中描述的这种简单设计可以满足并发量较小的系统要求,当并发量增大或者遇到恶意攻击时,则可能导致大量请求达到数据库导致数据库压力增大甚至不可用。为了最大可能的减小数据库压力,在使用 Redis 做缓存时,就需要做周全的设计,其中要解决的比较典型的问题就是缓存穿透,缓存击穿,缓存雪崩

1. 缓存穿透

缓存穿透是指用户不断请求缓存和数据库中都没有的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方案一:

  1. 接口层增加校验,如用户鉴权校验;
  2. 查询参数做合法性校验,如拦截 id 为负的查询;
  3. 参数通过合法性校验,但是数据不存在,这时也可以将 key-value 对写为 key-null 或者将 value 置成可识别的错误,同时设置一个较短的过期时间(设置太长会导致数据库插入了数据以至数据长时间不一致)。
    **解决方案二:**布隆过滤器。当查询请求进来时,可以先使用布隆过滤器进行判断,根据布隆过滤器的特性,判断不存在的数据一定不存在,判断存在的数据可能存在。如果通过布隆过滤器判断出请求的数据不存在,则可直接返回不存在,这样就拦截调了无效的请求。布隆过滤器的详细介绍可参考本系列的《Redis 系列–布隆过滤器》。这里介绍一下采用布隆过滤器的实现方式:
    方式一,客户端引入布隆过滤器,数据的计算和存储都放在客户端,这样可以最大限度的降低到达 Redis 的请求数量。
    方式二,客户端只引入计算逻辑,槽位数据的存储放在 Redis,客户端计算出要查询的 key 的指纹所在槽位后,只需到 Redis 进行查询,这样降低了 Redis 端的 CPU 消耗。
    方式三,Redis 端引入布隆过滤器模块,计算与存储都由其完成,这样可以做到客户端的瘦身。

2. 缓存击穿

缓存击穿是指大量请求同时查询缓存中不存在但数据库中有的数据(一般是缓存时间到期),这时由于并发量大,读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大。
解决方案:

  1. 设置热点数据永远不过期。
  2. 对数据库查询操作加互斥锁,具体方案如下:
    1)使用 setnx 命令获取互斥锁;
    2)获取锁成功,去数据库查询,将结果更新到缓存并释放锁;
    3)获取锁失败,说明已经有线程去查数据库,等待一段时间再查缓存。

加锁的方案中需要解决分布式锁的死锁问题。

3. 缓存雪崩

缓存雪崩是指缓存中大量数据同时到期,并发请求刚好需要查询这些数据,引起数据库压力过大。缓存击穿指少量过期的数据被并发查询,缓存雪崩是并发查大量不同的已过期的数据。
解决方案:

  1. 设置热点数据永远不过期。
  2. 如果需要过期的数据不是时点性的,可以设置随机的过期时间,防止同一时间大量数据过期现象发生。
  3. 如果需要过期的数据是时点性的,比如会定期更新的数据,可以将查询的请求延期处理。

参考文章:缓存穿透、缓存击穿、缓存雪崩区别和解决方案

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值