Redis高频八股文与解决策略

本文详细解释了Redis的特性,介绍了缓存穿透、缓存雪崩和缓存击穿的概念,并探讨了如何通过策略如参数校验、布隆过滤器等解决这些问题。此外,文章还讨论了保证缓存与数据库数据一致性的方式,包括各种更新缓存和数据库的方案及其优缺点。
摘要由CSDN通过智能技术生成

什么是Redis?

Redis是一个高性能的非关系型的键值对数据库,使用C编写实现的。与传统的数据库不同的是Redis是存在内存中的,所以读写速度非常快,每秒可以处理超过10万次的读写操作,这也是Redis常常被用作缓存的原因。

什么是缓存穿透?

缓存穿透是指用户的请求没有经过缓存而直接请求到数据库上了,比如用户请求的key在Redis中不存在,或者用户恶意伪造大量不存在的key进行请求,都可以绕过缓存,导致数据库压力太大挂掉。

如何解决缓存穿透?

  • 参数校验、屏蔽非法参数
    检验用户请求的合法性,屏蔽掉不合理的参数请求(无法彻底避免)

  • 缓存空值
    数据库查询为空,可以给缓存一个空值或默认值,防止第二次再去数据库(导致Redis缓存大量无效数据,甚至内存爆掉)

  • 布隆过滤器
    使用布隆过滤器快速判断数据是否存在,将所有可能存在的数据哈希存到一个足够大的容器中,不存在的数据被这个bitmap拦截掉(布隆过滤器存在一定误差,且不支持删除数据,可通过定期重构解决数据变更问题)

什么是缓存雪崩?

缓存雪崩是指在某一个时刻出现大规模的缓存失效的情况,大量的请求直接打在数据库上面,可能会导致数据库宕机,如果这时重启数据库并不能解决根本问题,会再次造成缓存雪崩。

为什么会造成缓存雪崩?

一般来说,造成缓存雪崩主要有两种可能

  • Redis宕机了
  • 很多key采取了相同的过期时间

如何解决缓存雪崩?

  • 为避免Redis宕机造成缓存雪崩,可以搭建Redis集群
  • 尽量不要设置相同的过期时间,例如可以在原有的过期时间加上随机数
  • 服务降级,当流量到达一定的阈值时,就直接返回“系统繁忙”之类的提示,防止过多的请求打在数据库上,这样虽然用户体验较差,但至少可以使用,避免直接把数据库搞挂

什么是缓存击穿?

缓存雪崩是大规模的key失效,而缓存击穿是一个热点的Key,有大并发集中对其进行访问,突然间这个Key失效了,导致大并发全部打在数据库上,导致数据库压力剧增,这种现象就叫做缓存击穿。

比较经典的例子是商品秒杀时,大量的用户在抢某个商品时,商品的key突然过期失效了,所有请求都到数据库上了。

如何解决缓存击穿?

  • 虑热点key不设置过期时间,避免key过期失效
  • 在数据的value上增加一个逻辑到期时间,数据查询发现过期后进行加锁,拿到锁的请求去查询数据库进行Redis更新,未抢到锁的线程可以返回过期前的默认数据,用弱一致性来避免缓存击穿

如何保证缓存与数据库双写时的数据一致性?

这是面试的高频题,需要好好掌握,这个问题是没有最优解的,只能数据一致性和性能之间找到一个最适合业务的平衡点

首先先来了解下一致性,在分布式系统中,一致性是指多副本问题中的数据一致性。一致性可以分为强一致性、弱一致性和最终一致性

  • 强一致性:当更新操作完成之后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。强一致性对用户比较友好,但对系统性能影响比较大。
  • 弱一致性:系统并不保证后续进程或者线程的访问都会返回最新的更新过的值。
  • 最终一致性:也是弱一致性的一种特殊形式,系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。
    大多数系统都是采用的最终一致性,最终一致性是指系统中所有的副本经过一段时间的异步同步之后,最终能够达到一个一致性的状态,也就是说在数据的一致性上存在一个短暂的延迟。

如果想保证缓存和数据库的数据一致性,最简单的想法就是同时更新数据库和缓存,但是这实现起来并不现实,常见的方案主要有以下几种:

  • 先更新数据库,后更新缓存
  • 先更新缓存,后更新数据库
  • 先更新数据库,后删除缓存
  • 先删除缓存,后更新数据库

乍一看,感觉第一种方案就可以实现缓存和数据库一致性,其实不然,更新缓存是个坑,一般不会有更新缓存的操作。因为很多时候缓存中存的值不是直接从数据库直接取出来放到缓存中的,而是经过一系列计算得到的缓存值,如果数据库写操作频繁,缓存也会频繁更改,所以更新缓存代价是比较大的,并且更改后的缓存也不一定会被访问就又要重新更改了,这样做无意义的性能消耗太大了。

下面介绍删除缓存的方案

先更新数据库,后删除缓存

这种方案也存在一个问题,如果更新数据库成功了,删除缓存时没有成功,那么后面每次读取缓存时都是错误的数据。
解决这个问题的办法是删除重试机制,常见的方案有利用消息队列和数据库的日志
但这个方案也有一些缺点,比如系统复杂度高,对业务代码入侵严重,这时可以采用订阅数据库日志的方法删除缓存。

先删除缓存,后更新数据库

这种方案也存在一些问题,比如在并发环境下,有两个请求A和B,A是更新操作,B是查询操作,假设A请求先执行,会先删除缓存中的数据,然后去更新数据库,B请求查询缓存发现为空,会去查询数据库,并把这个值放到缓存中,在B查询数据库时A还没有完全更新成功,所以B查询并放到缓存中的是旧的值,并且以后每次查询缓存中的值都是错误的旧值。

这种情况的解决方法通常是采用延迟双删,就是为保证A操作已经完成,最后再删除一次缓存

逻辑很简单,删除缓存后,休眠一会儿再删除一次缓存,虽然逻辑看起来简单,但实现起来并不容易,问题就出在延迟时间设置多少合适,延迟时间一般大于B操作读取数据库+写入缓存的时间,这个只能是估算,一般可以考虑读业务逻辑数据的耗时 + 几百毫秒。

在实际应用中,还是先更新数据库后删除缓存这种方案用的多些,因为延迟双删这个操作的颗粒度过于庞大,不太适合实际开发。

需要注意的是,无论哪种方案,如果数据库采取读写分离+主从复制延迟的话,即使采用先更新数据库后删除缓存也会出现类似先删除缓存后更新数据库中出现的问题,举个例子

  1. A操作更新主库后,删除了缓存
  2. B操作查询缓存没有查到数据,查询从库拿到旧值
  3. 主库将新值同步到从库
  4. B操作将拿到的旧值写入缓存

这就造成了缓存中的是旧值,数据库中的是新值,解决方法还是上面说的延迟双删,延迟时间要大于主从复制的时间。

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值