从根本上解决Redis和MySQL结果不一致的问题

📕我是廖志伟,一名Java开发工程师、Java领域优质创作者、优快云博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作者、产品软文创造者、技术文章评审老师、问卷调查设计师、个人社区创始人、开源项目贡献者。🌎跑过十五公里、徒步爬过衡山、🔥有过三个月减肥20斤的经历、是个喜欢躺平的狠人。

📘拥有多年一线研发和团队管理经验,研究过主流框架的底层源码(Spring、SpringBoot、Spring MVC、SpringCould、Mybatis、Dubbo、Zookeeper),消息中间件底层架构原理(RabbitMQ、RockerMQ、Kafka)、Redis缓存、MySQL关系型数据库、 ElasticSearch全文搜索、MongoDB非关系型数据库、Apache ShardingSphere分库分表读写分离、设计模式、领域驱动DDD、Kubernetes容器编排等。🎥有从0到1的高并发项目经验,利用弹性伸缩、负载均衡、报警任务、自启动脚本,最高压测过200台机器,有着丰富的项目调优经验。

📙经过多年在优快云创作上千篇文章的经验积累,我已经拥有了不错的写作技巧。同时,我还与清华大学出版社签下了四本书籍的合约,并将陆续在明年出版。这些书籍包括了基础篇、进阶篇、架构篇的📌《Java项目实战—深入理解大型互联网企业通用技术》📌,以及📚《解密程序员的思维密码–沟通、演讲、思考的实践》📚。具体出版计划会根据实际情况进行调整,希望各位读者朋友能够多多支持!

以梦为马,不负韶华

希望各位读者大大多多支持用心写文章的博主,现在时代变了,信息爆炸,酒香也怕巷子深,博主真的需要大家的帮助才能在这片海洋中继续发光发热,所以,赶紧动动你的小手,点波关注❤️,点波赞👍,点波收藏⭐,甚至点波评论✍️,都是对博主最好的支持和鼓励!

💡在这个美好的时刻,本人不再啰嗦废话,现在毫不拖延地进入文章所要讨论的主题。接下来,我将为大家呈现正文内容。

优快云

关于redis与数据库的数据一致性,业界使用最多的是数据同步问题(双删策略)

从根本上解决Redis和MySQL结果不一致的问题

🎉 双删策略

传说中的双删策略,听起来有点高大尚的样子,其实就是一种解决缓存和数据库数据不一致的问题的策略。接下来,让我们用生动有趣的例子和详细的解释来描述一下这种双删策略。

首先,我们来了解一下数据库和缓存的关系。在很多业务场景中,数据库是存储数据的唯一来源,而缓存则是为了提高读取数据的效率而存在的。因为数据库的读取速度相比缓存要慢很多,而且随着并发访问量的增加,数据库也会出现瓶颈,进而影响系统性能。那么,为了解决这个问题,我们可以先将数据存储到缓存中,当需要读取数据时,我们先从缓存中读取,如果没有再从数据库中读取。

但是,这种方案会带来一个问题:当数据库中的数据发生变化时,缓存中的数据却没有及时更新,导致缓存中的数据和数据库中的数据不一致。也就是说,当我们从缓存中读取数据时,可能得到的是旧的数据,而不是最新的数据。这种情况下,我们就需要考虑采用双删策略。

双删策略的核心思想是,先删除缓存中的数据,再更新数据库中的数据,然后再将新的数据写入缓存。这样,就可以保证缓存中的数据和数据库中的数据一致。

接下来,我们用一个例子来说明双删策略的执行过程。假设有两个请求 A 和 B,请求 A 进行了更新操作,请求 B 进行了查询操作。

  1. 请求 A 进行写操作,先删除缓存中的数据。

在这一步中,请求 A 先删除了缓存中的数据。

  1. 请求 B 查询缓存,发现缓存不存在。

由于请求 A 删除了缓存中的数据,因此请求 B 在查询缓存时,发现缓存中并没有请求所需要的数据。

  1. 请求 B 去数据库查询得到旧值。

既然缓存中没有数据,那么请求 B 就去数据库中查询请求所需要的数据,但是由于请求 A 还没有更新数据库中的数据,因此请求 B 得到的是旧的数据。

  1. 请求 B 将旧值写入缓存。

由于请求 B 得到的是旧的数据,因此它将旧的数据写入了缓存中。

  1. 请求 A 更新数据库中的数据。

在这一步中,请求 A 更新了数据库中的数据,这个数据就是我们需要的最新数据。

  1. 请求 A 将新值写入缓存。

现在,数据库中的数据已经更新了,请求 A 就将最新的数据写入缓存中,这样,缓存中的数据和数据库中的数据就保持了一致。

通过上述的例子,我们可以看出,双删策略的确可以解决缓存和数据库数据不一致的问题。在这个过程中,我们首先删除了缓存中的数据,保证了缓存和数据库中的数据一致性,然后再将最新的数据写入缓存,保证了读取数据的效率。当然,双删策略也有一些缺点,比如在高并发场景下可能会出现性能问题,但是,相比于数据不一致带来的问题,这些缺点都是可以接受的。

总之,双删策略是一种非常实用的解决缓存和数据库数据不一致问题的策略,它可以保证系统的高可用性和读取数据的高效性。虽然在实际应用中可能会带来一些性能问题,但是,相比数据不一致问题带来的风险,这些问题都是可以通过优化解决的。

🎉 延时双删策略

延时双删策略,这是一个用于解决数据库读写一致性问题的方案。相信很多同学都遇到过这种情况:在一个数据库读写分离架构中,一个请求进行更新操作,另一个请求进行查询操作,结果查询出来的数据却不是最新的,这就是因为数据还没来得及同步到从库,而查询请求却已经到达了从库,所以查询到的是旧值。

那么延时双删策略是怎么解决这个问题的呢?先来看看这个方案的执行步骤。

第一步,先淘汰缓存,这是因为如果不淘汰缓存,当下一次查询请求到来时,查询到的仍然是旧值,无法解决一致性问题。

第二步,将更新数据写入到数据库中,这一步和原来一样。

第三步,休眠1秒,再次淘汰缓存,这是关键步骤。1秒的时间是怎么得出来的呢?我们可以评估一下项目中读数据业务逻辑的耗时,在读数据业务逻辑的耗时基础上加上几百毫秒,就可以确保读请求结束,写请求可以删除读请求造成的缓存脏数据。

这样一来,因为我们在数据库中写入数据后,又休眠了1秒再次删除缓存,可以将在这1秒内可能造成的缓存脏数据再次删除掉,从而解决数据库读写一致性的问题。

但是,采用这种同步淘汰策略会降低吞吐量,那么怎么办呢?这时候又要引入一个异步删除的概念,即将第二次删除作为异步的。自己起一个线程,异步删除。这样,写的请求就不用沉睡一段时间后再返回,可以直接返回,从而加大吞吐量。

总结一下,延时双删策略是一种用于解决数据库读写一致性问题的方案,通过在写请求后延迟删除缓存来解决从库同步延迟带来的数据不一致问题,同时通过异步删除来提高吞吐量。希望这篇文章能够帮助到大家理解并应用这个实用的方案。

🎉 异步延时删除策略

异步延时删除策略。这个策略实际上是用来解决数据库读写操作中可能出现的数据不一致问题的。相信大家都知道,在数据库的读写操作过程中,如果不加以管理,就极有可能出现数据不一致的问题。因此,这个问题一直被广大 IT 从业者所关注。

现在,我们来看一个例子。假设有两个请求 A 和 B,其中请求 A 做查询操作,而请求 B 则做更新操作。那么,在这种情况下就有可能出现如下情形:缓存刚好失效;请求 A 查询数据库,得到一个旧值;请求 B 将新值写入数据库;请求 B 删除缓存;请求 A 将查到的旧值写入缓存。这个过程中,由于请求 B 的写数据库操作比请求 A 的读数据库操作耗时更短,因此存在一定几率,使得请求 B 的删除操作先于请求 A 的写入操作执行,从而导致脏数据的产生。

为了解决这个问题,我们可以给缓存设有效时间,不过这种方法有时并不总是可行的。因此,我们还可以采用异步延时删除策略。具体而言,在读请求完成后,我们可以将这个删除操作放到异步队列中,并在一定时间后再执行。这样,就可以保证读请求完成后再进行删除操作,从而避免了脏数据的产生。

不过,你可能会问,如果第二次删除失败怎么办呢?这是一个非常好的问题。实际上,如果第二次删除缓存失败,就会再次出现缓存和数据库不一致的问题。假设我们还是有两个请求 A 和 B,其中请求 A 进行更新操作,而请求 B 进行查询操作。首先,请求 A 进行写操作,删除缓存;接着,请求 B 查询发现缓存不存在,然后去数据库查询得到旧值;请求 B 将旧值写入缓存;请求 A 将新值写入数据库;最后,请求 A 试图去删除请求 B 写入的缓存值,但结果失效了。由此可见,如果第二次删除缓存失败,就会再次出现缓存和数据库不一致的问题。

综上所述,异步延时删除策略是一种非常有趣的技术,可以解决数据库读写操作中可能出现的数据不一致问题。当然,如果第二次删除缓存失败,就必须考虑一些其他的策略来解决数据不一致的问题。


作为一个喜欢玩游戏的程序猿,我们经常要面对一些恶心的数据库问题。比如我们现在有一张用户的信息表,里面有用户的ID、昵称、性别等等各种各样的信息。同时,我们还有一些缓存,用来存储用户信息的一些中间结果,方便查询。但是有一天,你突然发现,缓存和数据库里的数据已经不一致了!这可怎么办呢?

🎉 解决方案一:强制更新数据库数据并重试删除缓存操作

首先,我们可以强制更新数据库里的数据。比如说,有一个用户通过一个接口来修改了自己的昵称,这时候我们就需要把数据库中的昵称修改掉。但是,这样还不够,因为缓存中的昵称也需要更新,否则查询出来的结果可能还是旧的。所以我们需要把缓存也清除掉,重新查询数据库来更新缓存中的数据。但是,这里有一个大问题,就是缓存因为种种问题可能删除失败,导致数据仍然不一致。为了避免这种情况,我们可以采用一种更加可靠的方式——发送消息队列。

具体操作是,我们将需要删除的key发送至消息队列,然后自己消费消息,获得需要删除的key。接着,继续重试删除操作,直到成功为止。但是,这种方案也有一个非常明显的缺点——对业务线代码造成大量的侵入,因为我们需要在代码中加入发送消息和消费消息的逻辑。

🎉 解决方案二:订阅binlog程序+消息队列重试机制

为了避免解决方案一的弊端,在不侵入业务线代码的前提下,我们可以采用解决方案二。具体来说,我们可以启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。

具体操作是,当我们在应用程序中进行了一次数据库操作(比如添加一个用户),数据库会将操作信息写入binlog日志当中。这时候,订阅程序会提取出所需要的数据以及key,并将这些信息发送至消息队列。接着,另起一段非业务代码,获得该信息,并尝试删除缓存操作。但如果删除失败,这些信息会重新被发送至消息队列,以供重试操作。

这种方案非常可靠,因为它可以保证数据一致性,同时也能保证不侵入业务线代码的情况下进行操作。订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。重试机制,采用的是消息队列的方式,如果对一致性要求不是很高,直接在程序中另起一个线程,每隔一段时间去重试即可。

综上所述,对于数据库和缓存不一致的问题,我们可以采用两种不同的解决方案,分别为强制更新数据库数据并重试删除缓存操作、订阅binlog程序+消息队列重试机制。但是需要注意的是,这两种方案都有各自的优缺点,需要根据具体情况进行选择。

优快云

🔔如果您需要转载或者搬运这篇文章的话,非常欢迎您私信我哦~

希望各位读者大大多多支持用心写文章的博主,现在时代变了,信息爆炸,酒香也怕巷子深,博主真的需要大家的帮助才能在这片海洋中继续发光发热,所以,赶紧动动你的小手,点波关注❤️,点波赞👍,点波收藏⭐,甚至点波评论✍️,都是对博主最好的支持和鼓励!

📥博主的人生感悟和目标

探寻内心世界,博主分享人生感悟与未来目标

  • 🍋程序开发这条路不能停,停下来容易被淘汰掉,吃不了自律的苦,就要受平庸的罪,持续的能力才能带来持续的自信。我本身是一个很普通程序员,放在人堆里,除了与生俱来的盛世美颜,就剩180的大高个了,就是我这样的一个人,默默写博文也有好多年了。
  • 📺有句老话说的好,牛逼之前都是傻逼式的坚持,希望自己可以通过大量的作品、时间的积累、个人魅力、运气、时机,可以打造属于自己的技术影响力。
  • 💥内心起伏不定,我时而激动,时而沉思。我希望自己能成为一个综合性人才,具备技术、业务和管理方面的精湛技能。我想成为产品架构路线的总设计师,团队的指挥者,技术团队的中流砥柱,企业战略和资本规划的实战专家。
  • 🎉这个目标的实现需要不懈的努力和持续的成长,但我必须努力追求。因为我知道,只有成为这样的人才,我才能在职业生涯中不断前进并为企业的发展带来真正的价值。在这个不断变化的时代,我必须随时准备好迎接挑战,不断学习和探索新的领域,才能不断地向前推进。我坚信,只要我不断努力,我一定会达到自己的目标。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java程序员廖志伟

赏我包辣条呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值