解决缓存和数据库不一致的几种方案

1. 先写缓存再写数据库

两个用户购买了车站余票,假设余票有 17 张,两个用户扣减完还剩 15 张票。

如图所示,多个请求并发写入缓存和数据库,写请求 A 先更新 Redis 余票为 16。此时,写请求 B 将余票缓存更新 Redis 为 15,紧接着执行数据库更新为 15。这个时候,写请求 A 继续执行更新数据库操作,余票数据更新为 16。

这样就导致了多请求并发场景下,执行结果和咱们预期的结果不相符。

img

2. 先写数据库再写缓存

同上所诉,参考对应的业务场景和多请求并发场景,不同的是前者先更新缓存,后者先更新的是数据库,相同的是都存在并发问题,导致结果与预期并不相符。

img

3. 先删除缓存再写数据库

假设有两个并发的读写操作,一个是写操作,另一个是读操作。

  1. 并发读写的情况下,写操作首先删除缓存,接下来需要执行更新数据库操作。

  2. 读操作发生,由于缓存已经被删除,读操作不得不从数据库中读取数据。然而,由于写操作尚未完成,数据库中的数据仍然是过时的。

  3. 写操作这时需要更新数据库中的值,更新后 MySQL 数据库是最新的值。

  4. 读操作将从数据库中查询到的过时数据再回写到缓存。

在这种情况下,读操作获取到的是过时的数据,尽管写操作已经完成。因为缓存被删除,读操作不得不从数据库中读取旧值,而不是最新的值。

img

4. 先删除缓存再写数据库,再删除缓存

看着名字虽然有点长,但是如果换个词大家估计就懂了:缓存双删技术方案。

img

如果说上图的读请求回写缓存在写请求第二次删除缓存之前,那这种技术方案是比较好的,而且也不用引入过多复杂的中间件。

问题就在于,第二次删除缓存,不一定在读请求回写缓存之后。所以我们需要保证第二次删除要在请求回写缓存之后。

假设读请求回写缓存大概需要 300ms,那我们是否可以在写请求第二次删除缓存前进行一个延迟操作,比如睡眠 500ms 后再删除?这样就可以规避读请求回写缓存在第二次删除之后了。

这种方案理论上是可以的,不过把这个睡眠操作使用延迟队列或者引入三方消息队列去做。

最新技术架构流程如下所示:

img

如果消息队列更新缓存失败了呢?其实这一点还好,凭借消息队列客户端消费的重试规则,如果更新失败次数都达到客户端重试阈值还是不行,那一定是数据或者缓存中间件有问题。

当然,如果重试次数多了,也必然会面临缓存与数据库不一致的时间变长了,这个是需要清楚的。

通过该技术方案,可以很好达到缓存与数据库最终一致性。

5. 先写数据库再删除缓存

读请求第一次查询时,会查询到一个错误的数据,因为写请求还没有更新到缓存,写请求写入 MySQL 成功后会删除缓存中的历史数据。后续读请求查询缓存没有值就会再请求数据库 MySQL 进行重新加载,并将正确的值放到缓存中。

也就是说这种模型会存在一个很小周期的缓存与数据库不一致的情况,不过对于绝大多数的情况来说,是可以容忍的。除去一些电商库存、列车余票等对数据比较敏感的情况,比较适合绝大多数业务场景。

img

当然,这种模型也不是完全没问题,如果说恰巧读缓存失效了,就会出现这种情况。

img

当缓存过期(可能是缓存正常过期也可能是 Redis 内存满了触发清理策略)条件满足,同时读请求的回写缓存 Redis 的执行周期在数据库删除之前,那么就有可能触发缓存数据库不一致问题。

上面说的两种情况,缺一不可,不过能同时满足这两种情况概率极低,低到可以忽略这种情况。

6. 先写数据库,通过 BinLog 异步更新缓存

这种方案是我认为最终一致性最为值得尝试以及使用的。但是有一句话说的是没有绝对合适的技术,只有相对适合的技术,这种方案实现是也存在一些技术问题,稍后会给大家详细说明。

img

如果是扣减库存的方案,比如说你将列车余票扣减为 16,但是同时又有一个请求将列车余票扣减为 15,这个时候,扣减为 15 的这个请求先到消息队列执行,将缓存更新为余票 15,但是随之而来的是第一个请求余票为 16,会将缓存余票为 15 给覆盖掉。

类似于这种逻辑,会存在一些数据一致性的问题,需要我们通过其它技术手段完善,比如数据库添加版本号,或者根据最后修改时间等技术规避这些问题。

另外,如果在写入数据库余票 16 前,同时有个查询请求,也会存在数据库不一致问题。比如在写入数据库余票 16 前,将数据库余票 17 获取到,然后等消息队列更新到缓存余票 16 后,再将数据库余票 17 更新到缓存。

这种出问题的概率比较小,因为跨的周期太长了。也是类似于存在一个很小周期的数据不一致性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关系型数据库和NoSQL数据库 什么是NoSQL 大家有没有听说过“NoSQL”呢?近年,这个词极受关注。看到“NoSQL”这个词,大家可能会误以为是“No!SQL”的缩写,并深感愤怒:“SQL怎么会没有必要了呢?”但实际上,它是“Not Only SQL”的缩写。它的意义是:适用关系型数据库的时候就使用关系型数据库,不适用的时候也没有必要非使用关系型数据库不可,可以考虑使用更加合适的数据存储。 为弥补关系型数据库的不足,各种各样的NoSQL数据库应运而生。 为了更好地了解本书所介绍的NoSQL数据库,对关系型数据库的理解是必不可少的。那么,就让我们先来看一看关系型数据库的历史、分类和特征吧。 关系型数据库简史 1969年,埃德加•弗兰克•科德(Edgar Frank Codd)发表了划时代的论文,首次提出了关系数据模型的概念。但可惜的是,刊登论文的《IBM Research Report》只是IBM公司的内部刊物,因此论文反响平平。1970年,他再次在刊物《Communication of the ACM》上发表了题为“A Relational Model of Data for Large Shared Data banks”(大型共享数据库的关系模型)的论文,终于引起了大家的关注。 科德所提出的关系数据模型的概念成为了现今关系型数据库的基础。当时的关系型数据库由于硬件性能低劣、处理速度过慢而迟迟没有得到实际应用。但之后随着硬件性能的提升,加之使用简单、性能优越等优点,关系型数据库得到了广泛的应用。 通用性及高性能 虽然本书是讲解NoSQL数据库的,但有一个重要的大前提,请大家一定不要误解。这个大前提就是“关系型数据库的性能绝对不低,它具有非常好的通用性和非常高的性能”。毫无疑问,对于绝大多数的应用来说它都是最有效的解决方案。 突出的优势 关系型数据库作为应用广泛的通用型数据库,它的突出优势主要有以下几点: 保持数据的一致性(事务处理) 由于以标准化为前提,数据更新的开销很小(相同的字段基本上都只有一处) 可以进行JOIN等复杂查询 存在很多实际成果和专业技术信息(成熟的技术) 这其中,能够保持数据的一致性是关系型数据库的最大优势。在需要严格保证数据一致性和处理完整性的情况下,用关系型数据库是肯定没有错的。但是有些情况不需要JOIN,对上述关系型数据库的优点也没有什么特别需要,这时似乎也就没有必要拘泥于关系型数据库了。 关系型数据库的不足 不擅长的处理 就像之前提到的那样,关系型数据库的性能非常高。但是它毕竟是一个通用型的数据库,并不能完全适应所有的用途。具体来说它并不擅长以下处理: 大量数据的写入处理 为有数据更新的表做索引或表结构(schema)变更 字段不固定时应用 对简单查询需要快速返回结果的处理 。。。。。。 NoSQL数据库 为了弥补关系型数据库的不足(特别是最近几年),NoSQL数据库出现了。关系型数据库应用广泛,能进行事务处理和JOIN等复杂处理。相对地,NoSQL数据库只应用在特定领域,基本上不进行复杂的处理,但它恰恰弥补了之前所列举的关系型数据库的不足之处。 易于数据的分散 如前所述,关系型数据库并不擅长大量数据的写入处理。原本关系型数据库就是以JOIN为前提的,就是说,各个数据之间存在关联是关系型数据库得名的主要原因。为了进行JOIN处理,关系型数据库不得不把数据存储在同一个服务器内,这不利于数据的分散。相反,NoSQL数据库原本就不支持JOIN处理,各个数据都是独立设计的,很容易把数据分散到多个服务器上。由于数据被分散到了多个服务器上,减少了每个服务器上的数据量,即使要进行大量数据的写入操作,处理起来也更加容易。同理,数据的读入操作当然也同样容易。 提升性能和增大规模 下面说一点题外话,如果想要使服务器能够轻松地处理更大量的数据,那么只有两个选择:一是提升性能,二是增大规模。下面我们来整理一下这两者的不同。 首先,提升性能指的就是通过提升现行服务器自身的性能来提高处理能力。这是非常简单的方法,程序方面也不需要进行变更,但需要一些费用。若要购买性能翻倍的服务器,需要花费的资金往往不只是原来的2倍,可能需要多达5到10倍。这种方法虽然简单,但是成本较高。 另一方面,增大规模指的是使用多台廉价的服务器来提高处理能力。它需要对程序进行变更,但由于使用廉价的服务器,可以控制成本。另外,以后只要依葫芦画瓢增加廉价服务器的数量就可以了。 不对大量数据进行处理的话就没有使用的必要吗? NoSQL数据库基本上来说为了“使大量数据的写入处理更加容易(让增加服务器数量更容易)”而设计的。但如果不是对大量数据进行操作的话,NoSQ

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值