文章请你再次付款。或者网上购物,已经扣款了,却告诉我没有交易。这一系列的情况都是因为没有交易造成的。这说明了事务在生活中的一些重要性。做生意,你去小店买东西,就是你付钱送货。有了交易,你去网上购物,扣款产生订单交易。事务的具体定义事务提供了一种机制,将一个活动中涉及的所有操作合并为一个不可分割的执行单元。组成事务的所有操作只有在所有操作都能正常执行的情况下才能提交。只要有一个操作执行失败,就会导致整个事务回滚。简单地说,交易提供了一种“全有或全无”的机制。数据库本地事务ACID说到数据库事务,不得不说数据库事务ACID有四大特点:A:原子性(Atomicity)一个事务(transaction)中的所有操作要么全部完成,要么根本不完成,no它会在中间某处结束。如果事务执行过程中出现错误,则会回滚(Rollback)到事务开始前的状态,就好像事务从未执行过一样。就像你买东西,要么先付款后一起收货,要么不能发货,就退钱。C:一致性(Consistency)事务的一致性是指数据库在事务执行前后必须处于一致的状态。如果事务成功完成,则系统中的所有更改都将正确应用并且系统处于有效状态。如果事务发生错误,系统中的所有更改将自动回滚,系统返回到原始状态。I:隔离是指在并发环境下,当不同的事务同时操作同一个数据时,每个事务都有自己完整的数据空间。并发事务所做的修改必须与任何其他并发事务所做的修改隔离开来。一个事务查看数据更新时,数据的状态要么是另一个事务修改前的状态,要么是另一个事务修改后的状态,中间状态下事务不会查看数据。例如,您购买某物这一事实不会影响其他人。D:持久性(Durability)是指只要事务成功结束,它对数据库所做的更新就必须永久保存。即使发生系统崩溃,重启数据库系统后,数据库也可以恢复到事务成功结束时的状态。比如你买了东西,你需要记在账本上,即使老板忘记了,也会有据可查。InnoDB实现原理InnoDB是mysql的一个存储引擎。大多数人都熟悉mysql。这里简单介绍一下数据库事务实现的一些基本原理。在本地事务中,服务和资源可以看作是一个事务的封装下的:我们的本地事务是由资源管理器管理的:事务的ACID由InnoDB日志和锁来保证。通过数据库锁机制实现事务的隔离,通过重做日志(redolog)实现持久化,通过Undolog实现原子性和一致性。UndoLog的原理很简单。为了满足事务的原子性,在操作任何数据之前,先将数据备份到一个地方(存放数据备份的地方叫做UndoLog)。然后修改数据。如果发生错误或者用户执行了ROLLBACK语句,系统可以利用UndoLog中的备份将数据恢复到事务开始前的状态。与UndoLog相反,RedoLog记录的是新数据的备份。在事务提交前,只要持久化RedoLog即可,不需要持久化数据。当系统崩溃时,虽然数据没有被持久化,但是RedoLog已经被持久化了。系统可以根据RedoLog的内容,将所有数据恢复到原来的状态。对具体实现过程感兴趣的同学可以自行搜索扩展。什么是分布式事务?分布式事务是指事务的参与者、支持事务的服务器、资源服务器和事务管理器分别位于不同分布式系统的不同节点上。简单的说,一个大的操作是由不同的小操作组合而成的。这些小操作分布在不同的服务器上,属于不同的应用。分布式事务需要确保这些小操作要么全部成功,要么全部失败。本质上,分布式事务是为了保证不同数据库中数据的一致性。分布式事务的缘由从上面的本地事务来看,我们可以把它们看成两部分,一是服务产生多个节点,二是资源产生多个节点。服务多节点随着互联网的快速发展,微服务、SOA等服务架构模型正在被大规模使用。举个简单的例子,在一个公司内部,用户资产可能会被分成多个部分,比如余额和积分、优惠券等等。在公司里,有可能积分功能由一个微服务团队维护,优惠券由另一个团队维护。在这种情况下,无法保证抵扣积分后能否成功抵扣优惠券。多个资源节点也是如此。互联网发展太快了。一般来说,我们Mysql里面安装的数据,都是要分库分表的。对于支付宝转账业务,如果您给朋友转账,有可能您的数据库在北京,而您朋友的钱在上海,所以我们仍然不能保证他们同时成功。分布式事务的基础从上面的观点来看,分布式事务是随着互联网的飞速发展而应运而生的。这是必然的。我们之前说过,数据库的四个ACID特性已经不能满足我们的分布式事务了。这时,一些新的大佬们提出了一些新的理论:CAPCAP定理,又称布鲁尔定理。对于设计分布式系统(不仅仅是分布式事务)的架构师来说,CAP是你的入门理论。C(一致性):对于给定的客户端,读操作可以返回最准确的写操作。对于分布在不同节点上的数据,如果在某个节点上更新了数据,那么如果在其他节点上也能读取到最新的数据,就称为强一致性。如果有某个节点没有被读到,说明分布不一致。A(可用性):非故障节点在合理的时间内返回合理的响应(不是错误和超时响应)。可用性的两个关键是合理的时间和合理的响应。合理时间是指不能绝对阻止请求,应在合理时间内给予回报。合理的响应是指系统应该清楚地返回结果并且结果是正确的。这里正确的意思是,比如应该返回50,而不是40。P(partitiontolerance):当发生网络分区时,系统可以继续工作。比如这个集群有多台机器,一台机器网络有问题,但是集群还能正常工作。熟悉CAP的人都知道,三者不可共用。有兴趣的可以搜索一下CAP的证明。在分布式系统中,网络不可能是100%可靠的。分区其实是一个不可避免的现象。如果我们选择了CA,放弃了P,那么当出现分区的时候,为了保证一致性,此时请求必须被拒绝,但是A不允许,所以分布式系统理论上不可能选择CA架构,只有CP或AP架构。对于CP,放弃可用性,追求一致性和分区容错,我们的zookeeper其实是在追求强一致性。对于AP来说,放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性是很多分布式系统设计的选择,后续的BASE也是基于AP进行扩展。顺便说一句,在CAP理论中,网络延迟是被忽略的,即当一个事务被提交时,它被从节点A复制到节点B,但在现实中这显然是不可能的,所以总会有一定的时间因为不一致。同时在CAP中选择两个。比如你选择了CP,并不代表你放弃了A,因为P出现的概率太小了,所以很多时候你还是要保证CA。即使分区出现了,你也要为后面的A做准备,比如通过一些日志的手段,让其他机器恢复可用。BASEBASE是BasiclyAvailable(基本可用)、Softstate(软状态)和Eventuallyconsistent(最终一致性)这三个词组的缩写。它是AP在CAP中的扩展。基本可用性:当分布式系统发生故障时,允许失去部分可用功能,以保证核心功能可用。Softstate:允许系统存在一个不影响系统可用性的中间状态,这里指的是CAP中的不一致。最终一致性(FinalConsistency):最终一致性是指经过一段时间后,所有节点的数据都会达到一致性。BASE解决了CAP中没有网络延迟的理论,在BASE中使用软状态和最终一致性来保证延迟后的一致性。BASE和ACID是相反的。完全不同于ACID的强一致性模型。而是通过牺牲强一致性来获得可用性,允许数据在一段时间内不一致,但最终达到一致状态。分布式事务解决方案有了上面的理论基础,下面介绍一些常见的分布式事务解决方案。你真的需要分布式事务在谈解决方案之前,首先要确定你是否真的需要分布式事务?上面说了分布式事务有两个原因,其中一个是因为微服务太多了。我见过太多的团队一个人维护几个微服务,太多的团队设计过度,大家都累死了,微服务太多会导致分布式事务。此时,我不会推荐你使用以下任何一种方案,而是请将需要事务的微服务聚合成一个独立的服务,并使用数据库的本地事务。因为无论哪种方案都会增加你系统的复杂度,成本太高。不要因为追求某些设计而引入不必要的成本和复杂性。如果你确定需要引入分布式事务,可以看看下面几种常见的解决方案。2PC说到2PC,就不得不说到数据库分布式事务中的XATransactions。在XA协议中,分为两个阶段:第一阶段:事务管理器要求事务中涉及的各个数据库预先提交(precommit)这个操作,并反映是否可以提交。第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。优点:尽量保证数据的强一致性,实现成本低。所有主要数据库都有自己的实现。对于MySQL,从5.5开始就支持了。缺点:单点问题:事务管理器在整个流程中的作用非常关键。如果宕机,比如第一阶段已经完成,第二阶段快要提交的时候事务管理器宕机了。资源管理服务器会一直阻塞,导致数据库无法使用。同步阻塞:就绪后,资源管理器中的资源被阻塞,直到提交完成,资源被释放。数据不一致:虽然两阶段提交协议是为了分布式数据的强一致性而设计的,但是仍然存在数据不一致的可能性。只有部分参与者收到并执行了commit操作,其余参与者因为没有收到通知而被阻塞,此时出现数据不一致。总的来说,XA协议比较简单,成本较低,但其单点问题和无法支持高并发(由于同步阻塞)仍然是其最大的弱点。TCCTCC(Try-Confirm-Cancel)的概念最早是由PatHelland在2007年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文中提出的。与上面介绍的XA相比,TCC事务机制解决了几个缺点:1.解决了单一的协调点,主要业务方发起并完成本次业务活动。业务活动管理器也变得多点,引入集群。2、同步阻塞:引入超时,超时后补偿,不会锁住整个资源,将资源转化为业务逻辑形式,粒度变小。3.数据一致性。通过补偿机制,一致性由业务活动管理器控制。TCC解读:Try阶段:尝试执行,完成所有业务检查(一致性),预留必要的业务资源(准隔离)Confirm阶段:确认真正执行,不做任何业务检查,只使用预留的业务资源Try阶段,Confirm操作满足幂等性。需要幂等设计,Confirm失败后需要重试。Cancel阶段:取消执行,释放Try阶段预留的业务资源。Cancel操作满足幂等性。Cancel阶段的异常处理方案与Confirm阶段基本相同。举个简单的例子,如果你用100块钱买了一瓶水,Try阶段:你需要查看你的钱包是否有100块钱,并锁定这100块钱,水也是一样。如果失败就取消(释放100元和这瓶水),如果取消失败不管失败重试取消,所以要保持幂等性。如果全部成功,则确认,确认扣100元,这瓶水卖了,如果确认失败,不管是什么失败,重试(重试会依赖活动日志)适用于TCC:隔离性强,一致性要求严格的活动业务。执行时间短的业务实现参考:ByteTCC:https://github.com/liuyangming/ByteTCC/localmessagetable本地消息表这个方案最初是ebay提出的作为ebayhttps://queue.acm的完整方案。org/detail.cfm?id=1394128。该方案的核心是通过消息日志异步执行需要分布式处理的任务。消息日志可以存储在本地文本、数据库或消息队列中,可以自动或通过业务规则手动发起重试。人工重试更多应用于支付场景,通过对账系统处理事后问题。对于本地消息队列,核心是将大事务转化为小事务。还是拿上面用100元买一瓶水为例。1.扣钱的时候,需要在扣钱的服务器上新增一个本地消息表。需要把你扣的钱和存货减水写入本地消息表到同一个事务中(根据数据库的本地事务保证一致性。2.这时候有定时任务轮询本地事务表,将未发送的消息丢给商品库存服务器,让他减去水库存。先写入本服务器的交易表,然后进行扣减,扣减成功后,更新交易表中的状态3.商品服务器通过定时任务扫描消息表或者直接通知扣费服务器,扣费服务器本地消息表进行扣费状态更新4.对于一些异常情况,定时扫描已经存在的消息没有处理成功,重新发送,商品服务器收到消息后,先判断是否重复,如果收到,则判断是否重复判断是否执行。通知事务也被执行。如果没有执行,则需要重新执行。需要业务做到幂等,即不会多扣一瓶水。本地消息队列基于BASE理论,是一种最终一致性模型,适用于对一致性要求不高的。.在实现这个模型的时候,需要注意重试的幂等性。MQ事务在RocketMQ中实现了分布式事务。其实都是对本地消息表的一个封装,本地消息表是在MQ内部搬过来的。下面简单介绍一下MQ事务。如果想了解更多可以参考:https://www.jianshu.com/p/453c6e7ff81c。基本流程如下:第一阶段,Preparedmessage会得到消息的地址。第二阶段执行本地事务。第三阶段使用第一阶段获得的地址访问消息并修改状态。消息的接收者可以使用消息。如果确认消息失败,RocketMqBroker会提供一条消息,表明定时扫描尚未更新状态。如果有未确认的消息,它会发送消息给消息发送者,决定是否提交。在rocketmq中,以监听器的形式发送给发送者。,用于处理。如果消费超时,需要一直重试,消息接收端需要保证幂等性。如果消息消费失败,需要手动处理,概率很小。如果把这个复杂的过程设计成这么小的概率时间,那就得不偿失了。SagatransactionSaga是30年前一篇关于数据库伦理的文章中提到的一个概念。它的核心思想是将一个长事务拆分成多个本地短事务,由Saga事务协调器协调。如果正常结束,则正常完成。如果某个步骤失败,将以相反的顺序调用一次补偿操作。Saga的组成:每个Saga由一系列子事务Ti组成。每个Ti都有对应的补偿动作Ci,用于撤销Ti造成的结果。这里的每一个T都是一个本地事务。可以看出,与TCC相比,Saga没有“保留尝试”动作,其Ti是直接提交给库的。Saga有两种执行顺序:T1,T2,T3,...,TnT1,T2,...,Tj,Cj,...,C2,C1,其中0
