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