分布式事务是什么?
分布式事务是指事务的参与者、支持事务的服务器、资源服务器及事务的管理器分别位于分布式系统的不同节点上。对于传统的单机上的事务,所有的事情都在这一台机器上完成,而在分布式事务中,会有多个节点参与。
来看一个分布式事务的规范-XA,XA定义的三个组件:
- 应用程序(AP),它定义了事务的边界,并且定义了构成该事务的应用程序的特定操作。
- 资源管理器(RM),可以理解为一个DBMS系统,或者消息服务器管理系统。应用程序通过RM对资源进行控制,资源必须实现XA定义的接口。�RM提供了存储共享资源的支持。
- 事务管理器(TM),负责协调和管理事务,提供给AP编程接口并管理资源管理器。事务管理器监控事务的进程,负责处理事务的完成和失败。
事务管理器是额外引入的,之所以要引入事务管理器,是因为分布式系统中,两台机器理论上无法达到一致的状态,需要引入一个单点进行协调。
两阶段提交
两阶段提交协议,即2PC。分布式系统中,在提交之前增加了准备的阶段,所以称为两阶段提交。
特别地,在准备阶段有一个资源失败,那么第二阶段处理的就是回滚所有资源。
将提交分成两阶段进行的目的很明确,就是尽可能晚地提交事务,让事务在提交前尽可能地完成所有能完成的工作,这样,最后的提交阶段将是一个耗时极短的微小操作,这种操作在一个分布式系统中失败的概率是非常小的,也就是所谓的“网络通讯危险期”非常的短暂,这是两阶段提交确保分布式事务原子性的关键所在。(唯一理论上两阶段提交出现问题的情况是当协调者发出提交指令后当机并出现磁盘故障等永久性错误,导致事务不可追踪和恢复)
从两阶段提交的工作方式来看,很显然,在提交事务的过程中需要在多个节点之间进行协调,而各节点对锁资源的释放必须等到事务最终提交时,这样,比起一阶段提交,两阶段提交在执行同样的事务时会消耗更多时间。事务执行时间的延长意味着锁资源发生冲突的概率增加,当事务的并发量达到一定数量的时候,就会出现大量事务积压甚至出现死锁,系统性能就会严重下滑。这就是使用XA事务。
一阶段提交
不像两阶段提交那样复杂,一阶段提交非常直白,就是从应用程序向数据库发出提交请求到数据库完成提交或回滚之后将结果返回给应用程序的过程。一阶段提交不需要“协调者”角色,各结点之间不存在协调操作,因此其事务执行时间比两阶段提交要短,但是提交的“危险期”是每一个事务的实际提交时间,相比于两阶段提交,一阶段提交出现在“不一致”的概率就变大了。但是我们必须注意到:只有当基础设施出现问题的时候(如网络中断,当机等),一阶段提交才可能会出现“不一致”的情况,相比它的性能优势,很多团队都会选择这一方案。关于在spring环境下如何实现一阶段提交,有一篇非常优秀的文章值得参考:http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html?page=5
像一阶段提交这种模式,前提是应用程序能获取所有的数据源,然后使用同一个事务管理器(这里指是的spring的事务管理器)管理事务。这种模式最典型的应用场景非数据库sharding莫属。但是对于那些基于web service/rpc/jms等构建的高度自治(autonomy)的分布式系统接口,一阶段提交模式是无能为力的,此类场景下,还有最后一种方法可以帮助我们实现“最终一致性”,那就是事务补偿机制。
一般而言,需要交互的子系统数量较少,并且整个系统在未来不会或很少引入新的子系统且负载长期保持稳定,即无伸缩要求的话,考虑到开发复杂度和工作量,可以选择使用分布式事务。对于时间需求不是很紧,对性能要求很高的系统,应考虑使用一阶段提交或事务补偿机制。对于那些需要进行sharding改造的系统,基本上不应再考虑分布式事务,因为sharding打开了数据库水平伸缩的窗口,使用分布式事务看起来好像是为新打开的窗口又加上了一把枷锁。
大型网站一致性理的基础理论-CAP/BASE
我们来了解一下分布式系统的基础理论-CAP。
CAP理论:
一致性(C):所有的节点在同一时间读到同样的数据。当数据写入成功后,所有的节点会同事看到这个新的数据。
可用性(A):保证无论是成功还是失败,每个请求都能够收到一个反馈。这就是数据的可用性,重点是系统一定要有响应。
分区容忍性(P):即便系统中有部分问题或者消息的丢失,但是系统仍能够继续运行。
但是,分布式系统不能同时满足上面三项,一般来说,很多分布式系统在设计时的选择,是放弃一定的一致性,选择AP。
再来看看BASE:
基本可用:允许分区失败
软状态:接受一段时间的状态不同步
最终一致:保证最终的数据的状态是一致的
所以当我们选择了AP,那么对于C,采用的策略就是保证最终是一致的。
强一致:所有的节点在同一时间读到同样的数据。
最终一致:牺牲一部分的一致性,可以接受数据存在不一致的问题,但是通过重试或其他手段,保证数据最终会达到一致状态
Paxos协议
Paxos协议,是一个比两阶段提交要轻量的保证一致性的协议。
分布式系统中,要面临和单机处理完全不一样的问题,例如网络问题、进程or机器挂掉、进程超时等。这就会造成消息重复,一段时间内不可达等现象。Paxos协议是帮助我们解决分布式系统中一致性大问题的一个方案。
使用Paxos协议有一个前提,那就是不存在拜占庭将军问题。简单来说,就是要有一个可信的通信环境,所有信息都是准确的没有被篡改。
Paxos协议的决议过程比较复杂,这里不赘述,有兴趣可以自行了解。总结Paxos的核心原则就是少数服从多数。
但是Paxos协议存在一个问题,如果系统中同事有人提出议案,可能会出现碰撞导致失败。然后大家都是重试,重试仍然可能导致失败。这就会导致活锁。
解决的办法是在整个集群中设一个Leader,所有的议案都由他来提,这样就可以避免冲突。而引发的新问题就是如果Leader出问题了该如何处理,那就需要再选一个Leader出来。
总结
从工程上来说,如果能够避免分布式事务的引入,那还是避免为好;如果一定要引入分布式事务,可以考虑最终一致性的方法;从实现上来说,就是通过补偿的机制不断重试,让之前因为异常而没有进行到底的操作重试或继续进行,而不是回滚。
参考文献
大型网站系统与JAVA中间件实践
关于分布式事务、两阶段提交、一阶段提交和事务补偿机制的研究