给我五块钱,我给你讲讲分布式事务
简单介绍
数据库事务ACID:原子性,一致性,持久性,隔离性
分布式:是指在多台不同的服务器中部署不同的服务模块,通过远程调用协同工作,对外提供服务。
分布式事务:就是需要多个分布式服务同时为了一个功能实现而协调工作,我们想把这个实现的流程像本地事务那样,使其具有原子性。一致性。持久性和隔离性,重点在于多个服务间通过网络请求协同完成一个事务的过程;
五块钱知识
五毛钱理论:没有分布式事务的分布式架构就是儿豁
我们就举一个电商中的一个下单和减库存的列子来说明这个分布式事务问题
下单模块负责用户下单,库存模块负载增减库存,两个分布式服
那么问题来了,当我们的用户下单后,怎么通知库存模块减去相应库存呢?这里的着重点就在下单模块和库存模块能共协同工作,即我下单了,在订单数据库中已经插入了数据,那么库存模块必须给我把库存减了,必须保证这两个分布式服务的事务一致性
-
逻辑说明
用户下单完成,下单模块插入订单数据到订单表中,本地数据库事务支撑;
远程调用库存模块。减去相应库存数,网络远程请求可能会出毛病;
订单模块得到消息,减去相应商品库存并插入到数据库中,本地数据库事务支撑;
上面流程中1、3都使用Spring事务控制,抛出异常则单个服务回滚数据;
-
可能出现的问题
第一步出现异常,此订单无效,不会有后续操作,无异议;
第二步调用超时,迟迟不可返回数据,本地事务一直开启,影响数据库的性能,有异议;
第一步成功,第二步成功,第三部成功,但在订单模块事务提交的时候失败,造成订单没有生成,库存发生变化,有异议;
一块钱番外:CAP理论
以前写过,:https://www.cnblogs.com/msi-chen/p/11048553.html
copy过来让我们一起学习,使你这一块钱花的物超所值;
-
想要进行分布式事务控制,CAP理论是我们必须要知道的;
CAP原则:也叫CAP定理,指的是在一个分布式系统中,一致性、可用性、分区容错性三者不可兼得
一致性(Consistency)
分布式系统中的所有主机在同一时刻是否可以保证具有完全相同的数据备份,若具有,则该分布式系统具有一致性
可用性(Availability)
在集群中,部分节点发生故障后,是否会影响对客户端读写请求的响应,注意,若在短时间内会影响,其也不具有这里说说的"可用性"
分区容错性(Partition tolerance
分布式系统中的一台主机称为一个分区,分布式系统中各个主机无法保证数据的一致性,即不具有一致性是一种错误;分布式系统无法及时响应客户端请求,即不具有可用性也是一种错误;对于分布式系统,必须要具有对这些错误的"包容性",即容错性,这是必须得
-
为什么不能面面俱到
上面已经说明,分区容错性是分布式系统必须考虑的,此外,当我们想要保证高可用的时候,无非就是增加很多的节点,高可用的确是满足了,但带来的问题是,这么多的节点之间的数据同步是一个很多的消耗,极其容易造成数据不一致,当我们强调一致性的时候,节点越少,数据同步的消耗就小,但带来的节点少却又造成服务的可用性差,所以一致性和可用性是不可兼顾的,他们处于一个对立面;
-
CAP的组合
CA :不是分布式架构,就使用这种,关系数据库按照CA进行设计 ,放弃分区容错性,因为没有分区呀!!
AP:加强可用性和分区容错性,放弃立即一致性(强一致性),追求最终一致性,比如Eureka
比如微信提现,提示两个小时到账,而不是马上到账
CP:强调强一致性和分区容错性,放弃可用性,比如Zookeeper,master在宕机后选举Leader期间不提供服务
比如跨行转账,就是立即到账,你这边转出,那边收进,方认为一个事务才算完成
简单说说:先说结论,在分布式系统中AP运用的最多,因为他放弃的是强一致性,追求的是最终一致性,性价比最高,比如12小时内退款,微信2小时内提现到账,只要在用户的可接受范围内,保证一致性即可
三块五方案:解决方案
-
五毛五毛:两阶段提交协议(2PC)
为了解决分布式系统中的数据一致性而出现的两阶段提交协议
两阶段提交协议由协调者和参与者组成,经过两个阶段和三个操作保证一致性
Oracle、MySQL 均支持该协议;
应用程序连接两个数据源
应用程序通过事务协调器分别向两个库发起prepare(请求准备),两个库接到请求后开启本地事务,开始数据CRUD,准备成功回复Yes,否则回复No
事务协调器接收到两个库的回应,
如果都是Yes,此时向两个库发起提交事务,如若有一方提交失败,也由事务协调器发起事务回滚
如果一个库回应的是No,则分别向两个库发起事务回滚
2PC实现了强一致性,缺点就是事务协调者协调多个库的事务,事务执行时间拖长,性能底下
解决方案有:springboot+Atomikos or Bitronix
-
一块一块:事务补偿(TCC)
TCC事务补偿是基于2PC实现的业务层事务控制方案,它是Try、Confirm和Cancel三个单词的首字母
Try 检查及预留业务资源 :完成提交事务前的检查,并预留好资源。
Confirm 确定执行业务操作 :对try阶段预留的资源正式执行。
Cancel 取消执行业务操作 :对try阶段预留的资源释放。
还是订单模块和库存模块举列:
Try
下单业务是由订单模块和库存模块协同完成,咋填Try阶段,订单服务和库存服务完成检查和预留资源,订单服务检查档当前是否满足提交订单的条件(存在问完成的订单不允许再次重新添加新订单的),库存服务检查当前是否具有充足的库存,并锁定资源
Confirm
订单服务可库存服务成功完成Try阶段后,真是执行资源操作;
订单服务写入订单信息,库存服务减去相应库存
Cancel
如果有一方出现失败,则全部取消操作
订单模块需要删除插入的订单信息
库存模块需要将减去的库存还原
该模式的优缺点
最终保证数据的一致性,在业务实现事务的控制,灵活性较好
但是开发成本太高,每个事务操作每个参与者都要实现Try、Confirm、Cancel三个接口
-
两块两块:消息队列实现最终一致
此方案也是我想重点阐述的一个方案,是我写这篇博客的一个重点
该方案是将分布式事务拆分成多个本地事务来完成,并且通过消息队列的方式异步协调完成
我画了一个草图来说明:
订单服务和库存服务完成检查(不重复插入一个订单等...)
订单服务开启本地事务,写入订单表和消息表(消息表记载某一个商品,变动的库存数)
我们开启一个定时任务,使其扫描订单消息表,一旦有扫到结果,就将结果发给MQ
库存服务监听MQ,开启本地事务,将相应商品减去库存,并写入自己的消息表(商品id,库存number)
我们开启一个定时任务,使其扫描库存任务表,一旦有扫到结果,就将结果发给MQ
订单服务监听MQ,开启本地事务,将相应的数据从订单消息表中删除,
优缺点
异步的当时完成协调,性能较好,开发成本比TCC低
本地事务支撑,频繁读写数据库,影响数据库性能,不是应对高并发的最佳方案,但对于小并发却是一个优质的解决方案
可能再次补充...