大部分情况下我们所说的事务都是数据库事务(Database Transaction),后来延时到了非关系型数据库等其他领域,事务是运行在我们数据库上的一个逻辑工作单元,运行在工作单元中的所有sql都具有原子性的操作特点。
A:Atomic,原子性,事务必须是原子的工作单元,一个事务里面的所有操作要么全部成功,要么全部失败。
C:Consistency:一致性,事务完成时,保证所有数据的状态是一致性的。比如转账业务,有五个账户之间相互转账,无论转账多少次,怎么转账,五个账户的总余额都必须是不变的。
I:Isolation:隔离性,并发事务对同一资源所做的修改,事务之间所做的修改时隔离的,通过锁等手段实现。
D:Duration:持久性,事务完成之后,对系统的影响是永久性的,直到另一个事务执行改变其状态。
隔离性是通过锁实现的。
原子性、一致性、持久性是通过一些记录,比如事务日志、IO等实现。事务日志redo表示事务修改之后的状态记录,undo事务日志表示事务修改之前的状态记录,事务提交使用redo,事务回滚使用undo。
当你的系统是单机系统,并且使用单一数据库时,利用传统关系型数据库的事务特性来实现事务控制非常简单。但是,当你进行数据库分库分表、系统SOA话之后,传统关系型数据库的事务特性就不能满足事务需求了。
分布式事务指的是分布式环境下的事务。。。。
原本的单体系统,单数据库,配合spring的声明式事务加上数据库的事务特性可以很容易实现事务控制,但是,当业务量达到一定程度后,需要对系统SOA话。比如原本的一个电商系统,拆分成如下几个子系统。
此时一个生成订单的操作,分为两步,订单表新增一条记录,库存表某条记录库存数减库存。这个操作应当在同一事务上进行,并且此时两个操作处于不同系统、不同数据库中,此时就产生了一个分布式事务。
2PC和3PC都是强一致性的协议。
2PC(Two Phase Commit),二阶段提交,是计算机网络,尤其是在数据库领域内,为了使得基于分布式系统架构下所有节点在进行事务处理过程中能够保持原子性和一致性设计的一种算法。目前,绝大部分关系型数据库都是采用二阶段提交协议来完成分布式事务的处理,利用该协议能够非常方便地完成所有分布式事务参与者的协调,同一决定事务的提交或者回滚。从而能够有效地保证分布式事务的一致性,因此二阶段提交协议被广泛应用到许多分布式系统中。
二阶段提交就是把事务的提交分为两个阶段来执行。
协调者会根据参与者阶段一的反馈决定是提交事务还是回滚事务。
假设参与者有一个或者一个以上返回NO。
优点:原理简单、实现方便。
缺点:同步阻塞、单点问题、脑裂、太过保守。
同步阻塞:
二阶段提交最明显也是最大的一个问题是同步阻塞问题,这会极大限制了分布式系统的性能,在二阶段事务的执行过程中,所有参与者的逻辑都处于阻塞状态,也就是说各个参与者在等待其他参与者响应的过程中,都处于阻塞状态,占用着资源。在阶段1,可能某些节点的事务执行得快,所以向协调接反馈得快,某些节点的事务执行得慢,所以向协调接反馈得慢,而协调者要接收到所有参与者的反馈才会执行阶段二操作,此时事务执行得快的参与者即使事务已经执行完了并作出了反馈,依然会阻塞等待阶段二的来临。
单点问题:
一旦协调者出现问题,那么整个二阶段事务提交将无法进行,更为严重的是如果协调者在阶段二中出现问题,比如来不及向参与者发事务提交请求就宕机了,那么所有参与者都会处于事务资源的锁定当中,而无法完成事务操作。这个也是属于同步阻塞问题。
数据不一致:
在阶段二时,协调者向参与者发送Commit提交请求时,发生局部网络异常(协调者与部分参与者无法通信)或者信息还没向全部参与者发送(只发送了部分)就突然宕机时,导致只有部分参与者收到了Commit请求,收到了Commit请求的参与者进行事务提交,而没有收到的参与者无法进行事务提交,于是整个分布式系统便出现了数据不一致性现象。
太过保守:
如果协调者指示参与者进行事务提交询问过程中,参与者出现故障而导致协调者始终无法获取到所以参与者的响应信息的话,这时,协调者只能依靠自身的超时机制判断是否需要中断事务。这样的策略显得比较保守,换句话说2阶段提交协议没有设计较为完善的容错机制。任意一节点的失败都会导致整个事务的失败。
基于2PC提交协议遇到的问题,出现了3PC提交协议。即三阶段提交,把二阶段提交的阶段一“提交事务请求”拆分成两个阶段,形成由CanCommit、PreCommit、doCommit三个阶段组成的分布式事务处理协议。
在阶段二,协调者会根据参与者在阶段一的反馈情况来决定是否可以进行事务的Pre Commit操作。包含两种可能:
该阶段进行事务的真正提交。也会有两种情况出现。
注意:
跟2PC有一个明显不同点是,2PC只有协调者有超时机制,参与者是没有超时机制的,也就是2PC时在阶段二参与者没有收到协调者的Commit请求会一直阻塞,占用事务资源。
3PC提交,参与者也会有超时机制,在阶段三时,假设协调者宕机或者协调者与参与者之间出现网络问题,最终导致参与者无法及时接收来自协调的在第三阶段的doCommit或者abort请求或者第二阶段的preCommit请求时,参与者在等待超时后,会进行事务提交操作。
优点:
3PC解决了2PC的前两个问题,即阻塞和单点问题。但是还是没能解决数据一致性问题。但是进一步减小了数据不一致的概率。
解决阻塞问题是减少了参与者的阻塞范围,这个优化点,主要是避免了参与者在长时间无法与协调者节点通讯(协调者挂掉了)的情况下,无法释放资源的问题,因为参与者自身拥有超时机制会在超时后,自动进行本地commit从而进行释放资源。而这种机制也侧面降低了整个事务的阻塞时间和范围。
解决了单点问题:因为参与者在超时后会自动进行本地commit从而进行释放资源。所以当协调者宕机或者与参与者之间出现网络故障时,并且提交事务并释放资源,假设所有参与者都没有接收到协调者的阶段三的请求时,或者只接收了doCommit请求时,事务也能保持一致,因为接收到的参与者会执行提交操作,超时的参与者也会执行提交操作。
但是3PC也会出现数据一致性问题:
在阶段三:假设阶段二时,有一个或者一个以上的参与者反馈给协调者ACK反馈,或者协调者在超时时间内没有收到所有参与者的ACK反馈,协调者在阶段三就会向参与者发送abort请求,但是由于协调者与部分参与者存在网络故障或者协调者在发送给了部分参与者abort请求后就宕机了,导致部分参与者由于超时而提交事务,部分参与者由于接收到了abort请求而回滚事务,这就导致了不一致问题。
减小了数据不一致的概率:
在2PC提交协议,在第二阶段无论协调者发送Commit请求还是abort请求给参与者,只要出现由于协调者与部分参与者存在网络故障或者协调者在发送给了部分参与者abort请求后就宕机了的情况,就会导致数据不一样。
在3PC提交协议,在第三阶段,如果出现由于协调者与部分参与者存在网络故障或者协调者在发送给了部分参与者abort请求后就宕机了的情况的这个情况,当能接收到协调者消息的部分参与者接收到的消息是doCommit消息,数据还是能够保持一致的,因为超时的参与者也是执行提交操作,只有当协调者发送给部分参与者的消息是abort请求时,才会出现数据不一致。
以上两个协议都不能完全解决数据一致性问题,还得通过一些补偿机制来实现事务一致性。
X/OpenDTP事务模型:
X/OpenDTP(X/Open Distributed Transaction Procession Reference Model):是X/Open这个组织定义的一套分布式事务的标准,也就是定义了规范的API接口,由各个厂商进行具体实现。
这个标准提出了使用二阶段提交来保证分布式事务的完成性,后来J2EE也遵循了这套规范,设计并实现了java里的分布式事务编程接口规范-JTA。
X/A事务是 X/Open DTP定义的中间件与数据库之间的接口规范。X/A接口函数由数据库厂商提供。
X/OpenDTP事务模型的角色:
X/OpenDTP事务模型有三个角色:
AP:Application,也就是我们的应用系统。
RM:Resources Manager,资源管理器,也就是我们的数据库。
TM:Transaction Manager,事务管理器、事务协调者。负责分布式事务的协调。
流程就与2PC提交一样。AP类比参与者,TM类比协调者。
Java X/OpenDTP事务模型的实现:
这篇就不展开讲这两个了。