(五)Redis 缓存异常、应对策略

1、缓存和数据库不一致

只要我们使用 Redis 缓存,就必然会面对缓存和数据库间的一致性保证问题,这里的"一致性"包含了两种情况:缓存中有数据且与数据库中的值相同、缓存中没有数据,最新值在数据库中。

对于读写缓存来说,要想保证缓存和数据库中的数据一致,就要采用同步直写策略,在业务应用中使用事务机制,来保证缓存和数据库的更新具有原子性。对数据一致性的要求不高的场景,可以使用异步写回策略。

只读缓存在新增数据时是符合数据一致性第二种情况的,但是在删改数据时,无论是操作缓存还是操作数据库,有一项失败,就会产生数据不一致的问题。具体情况如图:
针对这种情况可以引用重试机制来解决,具体来说,可以把要数据暂存到消息队列中。无论哪项操作失败,都可以从消息队列中重新读取这些值,然后再次进行删除或更新。如果删改成功,就把数据从消息队列中去除,以免重复操作,以此来保证数据一致性。多次重试仍然失败的话就需要业务层面预警来排查解决了。

除了操作失败的原因,实际当有大量并发请求时,应用还是有可能读到不一致的数据。根据删改缓存、数据库的先后顺序分为两种情况:先操作缓存和先操作数据库。通过引用"延迟双删"的操作,来保证先操作缓存后操作数据库的数据一致性问题,代码示意如下:

复制代码
redis.delKey(X)
db.update(X)
Thread.sleep(N)
redis.delKey(X)

具体情况与应对措施总结如图:

2、雪崩

指大量的应用请求无法在 Redis 缓存中进行处理,到达数据库层面,导致数据库的压力激增。原因有二:
其一,缓存中有大量数据同时过期。
其二,Redis 缓存实例挂了。

原因一可以通过以下两个方法解决:
(1)微调过期时间:对于同一批数据设置不同的过期时间,如通过随机数延迟过期等。
(2)降级处理:非核心数据请求直接返回 空 或 错误 等预置信息,核心数据运行未命中缓存访问数据库。

原因二可以通过以下两个方法解决:
(1)熔断:拒绝缓存客户端的请求,保护数据库,防止整个系统崩溃,直到 Redis 缓存实例恢复正常,但是对业务应用的影响范围大。
(2)限流:允许部分请求到达 Redis 缓存,无法命中再访问数据库,减轻数据库压力,相对于熔断来说影响范围稍微缩小。
无论采取何种措施,雪崩都已经发生了,必定会影响到业务系统,所以要做好预防工作,构建 Redis 缓存高可靠集群,尽量避免事故。

3、击穿

指针对某个热点数据的请求,无法在缓存中处理,大量访问该数据的请求发送到了后端数据库,压力激增影响到其他请求,如下图:
为了避免缓存击穿给数据库带来的激增压力,对于访问特别频繁的热点数据,可以不设置过期时间了,全部在缓存中进行处理。

4、穿透

指要访问的数据既不在 Redis 缓存中,也不在数据库中,导致请求压力还是落到数据库,缓存也就成了"摆设"。如下图:
通常情况是由于业务数据被误删除,或者恶意攻击访问,有三种方案解决缓存穿透的影响:
(1)缓存空值或缺省值:针对穿透的数据,在 Redis 中缓存一个空值或是和业务层协商确定的缺省值,避免把大量请求发送给数据库。
(2)布隆过滤器
布隆过滤器由一个初值都为 0 的 bit 数组和 N 个哈希函数组成,数据入库则进行标记,过程如下:

  • 使用 N 个哈希函数,分别计算这个数据的哈希值,得到 N 个哈希值。
  • 我们把这 N 个哈希值对 bit 数组的长度取模,得到每个哈希值在数组中的对应位置。
  • 我们把对应位置的 bit 位设置为 1,这就完成了在布隆过滤器中标记数据的操作。

当查询某个数据时,按照哈希函数的计算结果,查看 bit 数组中这 N 个位置上的 bit 值,只要有一个不为 1,这就表明布隆过滤器没有对该数据做过标记。当缓存穿透发生,布隆过滤器的快速检测特性可以帮数据库阻挡大部分的压力。例子如下图:
(3)前端请求过滤:在请求入口前端进行合法性检测,把恶意的请求(如请求参数不合理、非法值或请求字段不存在)直接过滤掉。

小结

雪崩、击穿、穿透,这三类异常问题从成因来看,前两个主要是因为数据不在缓存中了,而穿透则是因为数据既不在缓存中,也不在数据库中。当雪崩或击穿发生时,一旦数据库中的数据被再次写入到缓存后,应用又可以在缓存中快速访问数据了,数据库的压力也会相应地降低,而穿透发生时,Redis 缓存和数据库会同时持续承受请求压力。

对应的熔断、降级、限流这些方法都是属于"有损"方案,在保证数据库和整体系统稳定的同时,会对业务应用带来负面影响。降级时,有部分数据的请求就只能得到错误返回信息,无法正常处理。熔断时,整个缓存系统暂停,影响的业务范围更大。流机时,整个业务系统的吞吐率会降低,并发处理能力减弱,影响到用户体验。

所以,应当尽量使用预防式方案,总结如下:

  • 雪崩,合理地设置数据过期时间,以及搭建高可靠缓存集群。
  • 击穿,在缓存访问非常频繁的热点数据时,不要设置过期时间。
  • 穿透,提前在入口前端实现恶意请求检测,或者规范数据库的数据删除操作,避免误删除。
相关推荐
黑贝是条狗4 天前
对.net 的改变
.net core
小吴同学·9 天前
NET6 WebApi第5讲:中间件(源码理解,俄罗斯套娃怎么来的?);Web 服务器 (Nginx / IIS / Kestrel)、WSL、SSL/TSL
中间件·c#·.net·.netcore·.net core
坐望云起10 天前
ASP.NET Web的 Razor Pages应用,配置热重载,解决.NET Core MVC 页面在更改后不刷新
前端·后端·asp.net·mvc·.net core·razor pages
代码拾光22 天前
.NET Core 中如何实现缓存的预热?
.net core
EdisonZhou1 个月前
基于Microsoft.Extensions.AI核心库实现RAG应用
llm·aigc·.net core
时光追逐者1 个月前
一个开源且免费的 .NET CMS 和应用程序框架
开源·c#·.net·cms·.net core
EdisonZhou1 个月前
基于Microsoft.Extensions.VectorData实现语义搜索
llm·aigc·.net core
时光追逐者1 个月前
推荐几款开源免费的 .NET MAUI 组件库
microsoft·开源·c#·.net·.net core·maui
EdisonZhou1 个月前
.NET程序员AI开发基座:Microsoft.Extensions.AI
aigc·.net core