分布式事务
简单说:某一个操作同时需要向多台机器的数据库上插入数据,这个操作如果成功了,则所有数据插入数据库成功,问题在于某一台机器上的数据库数据插入失败,其他数据库都插入成功,那么会怎么办?正常应该回滚数据库插入,已经成功的操作也应该回滚,这就是分布式事务。这涉及到多台机器上的不同操作,一同成功,否则就一同失败。
分布式需要考虑的问题-CAP理论及我的认识
新的需求,新的挑战,传统本地事务是基于本地数据库实现的,但是在多服务器上,需要新的指导思想,于是有了CAP理论。
CAP定理,又被叫作布鲁尔定理。对于设计分布式系统来说(不仅仅是分布式事务)的架构师来说,CAP就是你的入门理论:
- C (一致性):对某个指定的客户端来说,读操作能返回最新的写操作。对于数据分布在不同节点上的数据上来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致,同样一个事务未完全提交成功,客户端读取不到未提交事务中的数据,一致性强调客户端读操作能够获取最新的写操作结果。
- A (可用性):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。合理的时间指的是请求不能无限被阻塞,应该在合理的时间给出返回。合理的响应指的是系统应该明确返回结果并且结果是正确的,这里的正确指的是比如应该返回“success”或者“error”,而不是返回40。不能超时,不能出错。
- P (分区容错性):当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。
以上是一些理论,如何来理解呢?我个人有一些理解:
-
一致性
一开始以为这个一致性是最终数据的一致性,就是说一个分布式事务中各个操作要么全部成功,要么全部失败,按照本地事务理论,其实这个是非常重要的,但是在分布式环境里,因为有多个节点,多个数据备份,似乎这个一致性是指所有数据备份的一致性,这就是强一致性
,强一致性可以保证数据对外都是最新的,但是对可用性有所损失,因为要等到所有数据都同步之后才可以返回请求结果,相对的,弱一致性
就是说只要求主节点都是一致性的就行,备份节点的一致性就靠主从机制来保证,分布式事务并不用去保证这个主从的一致性,似乎弱一致性也就可以满足我们的要求了,但我们依旧希望的是数据都是一致性的。 -
可用性
可用性理解为就是一个请求应该在一定时间内都有一个返回值,高可用就是要求这个返回值尽可能是成功的,也就是说容忍少许的不成功返回,因为在分布式环境里,不允许你因为某种原因,比如10个节点,一个节点挂了,另外9个节点就不提供服务了,这样是有问题的,你在分布式系统里必须尽可能保证服务在几个节点挂了之后,依旧可以提供服务,给你一些时间去修复挂掉的节点,保证服务不中断,但要求这个服务是短时间内的,总不能一个小时提供一个返回结果出去,那种。 -
分区容错性
分区是一个什么概念呢?如果有10台机器互联,互相可以访问,有一天,突然有1台机器访问不了其他9台机器,但这一台机器可以提供服务,另外9台依旧可以提供服务,此时就造成了分区,服务器被分成两组来提供服务,如果都可以提供服务成功,那么对于读操作来说自然没什么问题,如果是写操作呢?一个用户18:00时刻修改了数据,这个数据被存到了A分区,20:00的时刻该用户又修改了数据,这个数据被存到了B分区,那么两者就数据就不一致了,所以分区造成的问题似乎很严重,所以要求分区具有容错性,且最好是强容错性。
通过上述分析,首先来讲一些分区高容错性,当出现分区的时候,我们希望数据可以同步到各个节点上,但此时似乎网络是不通的,网络不通,数据就无法同步,此时如果一个写操作请求过来了,访问A分区,
- 如果系统同意写操作,则会早造成数据不一致,但保证了高可用性。特别是当主节点被分区后,这个问题会非常严重,从节点被分区了,系统依旧可以提供写操作,等从节点恢复后,可以通过数据一致性操作来保证最终从节点与主节点数据一致。
- 如果拒绝写操作,则数据一致性保证了,但是高可用保证不了,所有的写请求全部放弃,不做处理,整个系统就完全是可读的。
在本地事务中数据库事务的ACID可以保证我们的数据是强一致性的,但是分布式事务中,强一致性带来的性能问题会比较大,造成响应缓慢,于是很多博客中认为应该将尽可能的保证可用性和分区容错性。因为分布式环境,首先要保证可以接受容错,不然机器宕机、服务挂掉,这些因素会造成服务更加不可用,而可用性可以保证服务快速的得到一个结果,返回给用户,所以一个分布式事务可以是弱一致性、高可用、分区高容错性
,一致性被牺牲了,只保证在一定时间内主节点数据与多副本数据是一致性的,一定时间内就有了缓冲的时间。下面是网上网友们给的一个通俗理解:
什么是具备 P 的系统呢,就是你的存储系统是分布式的。当发生了网络分区怎么办?这时候有两个选择,一就是要一致性不要可用性(CP),二就是要可用性不要一致性(AP)。发生了 网络分区之后你还能有在 C 和 A 之间选择的余地,因为你有得选择,所以你能容忍网络分区,所以你具备分区容忍性。
一些例子来看看:
- Zookeeper:CP原则,保证了一致性,集群搭建的时候,某个节点失效,则会进行选举行的leader,或者半数以上节点不可用,则无法提供服务,因此可用性没法满足
- Eureka:AP原则,无主从节点,一个节点挂了,自动切换其他节点可以使用,去中心化
分布式事务难点
如果你要设计一个分布式事务,你需要解决一下难点:
-
事务的原子性
事务操作跨不同数据节点,当多个节点操作中某一节点操作失败时,需要保证多节点操作的**要么什么都不做,要么做全套(All or Nothing)**的原子性。 -
事务的一致性
当发生网络传输故障或者节点故障时,节点间数据同步中断,某些节点难以复制该操作,在进行事务操作时需要保证数据一致性,保证事务的任何操作都不会使得数据违反数据库定义的约束、触发器等规则。 -
事务的隔离性
事务隔离性的本质就是如何正确多个并发事务的处理的读写冲突和写写冲突,因为在分布式事务控制中,可能会出现提交不同步的现象,这个时候就有可能出现“部分已经提交”的事务。此时并发应用访问数据如果没有加以控制,有可能出现“脏读”问题。
目前实现分布式事务的方案
XA事务
XA 事务是基于两阶段提交协议的,所以需要有一个事务协调者(transaction manager)来保证所有的事务参与者都完成了准备工作(第一阶段)。如果事务协调者(transaction manager)收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。MySQL 在这个XA事务中扮演的是参与者的角色,而不是事务协调者(transaction manager)。
缺点:XA的性能很低。一个数据库的事务和多个数据库间的XA事务性能对比可发现,性能差10倍左右。因此要尽量避免XA事务,例如可以将数据写入本地,用高性能的消息系统分发数据。或使用数据库复制等技术。只有在这些都无法实现,且性能不是瓶颈时才应该使用XA。
优点:保持了一致性。
2PC协议:全称Two Phase Commitment Protocol
- 阶段一为准备(prepare)阶段:即所有的参与者准备执行事务并锁住需要的资源。参与者ready时,向transaction manager报告已准备就绪。
- 阶段二为提交阶段(commit):当transaction manager确认所有参与者都ready后,向所有参与者发送commit命令。
优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)。
缺点:
- 性能问题
所有参与者在事务提交阶段处于同步阻塞状态,占用系统资源,容易导致性能瓶颈。 - 可靠性问题
如果协调者存在单点故障问题,如果协调者出现故障,参与者将一直处于锁定状态。 - 数据一致性问题
在阶段2中,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就导致了节点之间数据的不一致。
3PC协议
相比较2PC阶段
- 多了一个阶段,这个阶段是
pre-commit
,此阶段主要是为了防止协调者与参与者之间网络中断不停延时做处理,相当于二次确认,第三阶段是第二次确认,如果pre-commit
阶段数据已经改造完成,即使第三阶段网络出现问题,参与者也会执行commit。 - 超时机制
在参与者、协调者之间加了超时机制,超时之后,直接中断。
总结:
-
优点
相比二阶段提交,三阶段贴近降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段3中协调者出现问题时,参与者会继续提交事务。 -
缺点
数据不一致问题依然存在,当在参与者收到preCommit请求后等待do commite指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
TCC概念
TCC(Try-Confirm-Cancel)的概念,最早是由Pat Helland于2007年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。
TCC是服务化的二阶段编程模型,其Try、Confirm、Cancel 3个方法均由业务编码实现;
- Try操作作为一阶段,负责资源的检查和预留。
- Confirm操作作为二阶段提交操作,执行真正的业务。
- Cancel是预留资源的取消。
TCC事务的Try、Confirm、Cancel可以理解为SQL事务中的Lock、Commit、Rollback。
其实有点类似2PC方案,不过它的粒度小了很多,
TCC事务机制相对于传统事务机制(X/Open XA),TCC事务机制相比于上面介绍的XA事务机制,有以下优点:
- 性能提升
具体业务来实现控制资源锁的粒度变小,不会锁定整个资源。 - 数据最终一致性
基于Confirm和Cancel的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。 - 可靠性
解决了XA协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动管理器也变成多点,引入集群。
缺点:
TCC的Try、Confirm和Cancel操作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。
消息事务+最终一致性
所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败,开源的RocketMQ就支持这一特性。
分布式事务方案总结
以上提出了很多方案,但方案设计都是在事务级别,并没有涉及到我们之前讨论的问题,比如出现了分区,这些事务该如何处理,该不该继续写操作事务,分区出现后,一个用户先后出现了两个写操作,并且在不同的分区里,这应该如何操作,数据肯定不一致了,似乎这些分布式事务没有考虑这些问题,还是说,这些比较底层的操作应该让底层来保证,上层事务不需要考虑这些问题,如果不考虑这些问题,那之前强调CAP理论是为何?虽然很多资料强调放弃事务的强一致性,那么是不是默认写操作被禁止了,只有读操作来被允许?这些问题似乎并没有在某些资料中强调出来,所以我这里想思考这些问题。
方案总结:
-
2PC/3PC
依赖于数据库,能够很好的提供强一致性和强事务性,但相对来说延迟比较高,比较适合传统的单体应用,在同一个方法中存在跨库操作的情况,不适合高并发和高性能要求的场景。 -
TCC
适用于执行时间确定且较短,实时性要求高,对数据一致性要求高,比如互联网金融企业最核心的三个服务:交易、支付、账务。 -
本地消息表/MQ事务
都适用于事务中参与方支持操作幂等,对一致性要求不高,业务上能容忍数据不一致到一个人工检查周期,事务涉及的参与方、参与环节较少,业务上有对账/校验系统兜底。
后续持续更新 。。 。
参考博客
理解分布式事务
分布式事务
聊聊分布式事务,再说说解决方案
再有人问你分布式事务,把这篇扔给他
分布式事务
CAP 理论常被解释为一种“三选二”定律,这是否是一种误解?