目录
顺序节点
创建一个用于发号的节点“/test/lock",然后以他为父亲节点的前缀为”/test/lock/seq"依次发号:
获得最小号得锁
由于序号的递增性,可以规定排号最小的那个获得锁。所以,每个线程在尝试占用锁之前,首先判断自己是排号是不是当前最小,如果是,则获取锁。
节点监听机制
每个线程抢占锁之前,先抢号创建自己的ZNode。同样,释放锁的时候,就需要删除抢号的Znode。抢号成功后,如果不是排号最小的节点,就处于等待通知的状态。等谁的通知呢?不需要其他人,只需要等前一个Znode的通知就可以了。当前一个znode删除的时候,就是轮到了自己占用锁的时候。第一个通知第二个,第二个通知第三个,击鼓传花似的依次向后。
一个事务有四个基本特性,也就是我们常说的(ACID):
Atomicty(原子性):事务是一个不可分割的整体,事务内所有的操作要么全做成功,要么全失败。
Consistency(一致性):事务执行前后,数据从一个状态到另一个状态必须是一致的(A向B转账,不能出现A扣了钱,B却没收到)。
Isolation(隔离性):多个并发事务之间相互隔离,不能互相干扰。
Durability(持久性):事务完成后,对数据库的更改是永久保存的,不能回滚。
XA协议是一个基于数据库的分布式事务协议,其分为两部分:事务管理器和本地资源管理器。事务管理器作为一个全局的调度者,负责对各个本地资源管理器统一号令提交或者回滚。二阶段提交协议(2PC)和三阶段协议(3PC)就是根据此协议衍生出来而来。主流的诸如Oracle,MySQL等数据库均已实现了XA接口。
XA接口是双向的系统接口,在事务管理器以及一个或者多个资源管理器之间此女工程通信桥梁。也就是说,在基于XA的一个事务中,我们可以针对多个资源进行事务管理,例如一个系统访问多个数据库,或即访问数据库,又访问像消息中间件直接实现全部提交,或全部取消的事务。XA规范不是Java规范,而是一种通用的规范。
两段提交顾名思义就是要进行两个阶段的提交:
第一阶段,准备阶段(投票阶段);
第二阶段,提交阶段(执行阶段)。
下面还那下单扣库存举例子,简单描述一下两段提交(2PC)的原理:
之前说过业务服务化(SOA)以后,一个下单流程就会用到多个服务,各个服务都无法保证调用的其他服务的成功与否,这个时候就需要一个全局的角色(协调者)对各个服务(参与者)进行协调。
一个下单请求过来通过协调者,给每一个参与者发送Prepare消息,执行本地数据脚本但不提交事务。
如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中被占用的资源,显然2PC做到了所有操作要么全部成功,要么全部失败。
两段提交(2PC)的缺点:
二阶段提交看似能够提供原子性的操作,但他存在着严重的缺陷:
网络都懂导致的数据不一致:第二阶段中协调者向参与者发送Commit命令之后,一旦此时发生网络抖动,导致一部分参与者接收到了Commit请求并执行,可其他未接到Commit请求的参与者无法执行事务提交。进而导致整个分布式系统出现了数据不一致。
超时导致的同步阻塞问题:
2PC中的所有的参与者节点都为事务阻塞型,当某一个参与者节点出现通信超时,其余参与者都会被动阻塞占用资源不能释放。
单点故障的风险:
由于严重的依赖协调者,一旦协调者发生故障,而此时参与者还都处于锁定资源的状态,无法完成事务commit操作。虽然协调者出现故障后,会重新选举一个协调者,可无法解决因前一个协调者宕机导致的参与者处于阻塞状态的问题。
三阶段提交(3PC)是对两段提交(2PC)的一种升级优化,3PC在2PC的第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前,各参与者节点的状态都一致。同时在协调者和参与者中都引入超时机制,当参与者各种原因未收到协调者的Commit请求后,会对本地事务进行Commit,不会一直阻塞等待,解决了2PC的单点故障问题,但3PC还是没能从根本上解决数据一致性的问题。
3PC的三个阶段分别是CanCommit,PreCommit,DoCommit:
CanCommit:协调者向所有参与者发送CanCommit命令,询问是否可以执行事务提交操作。如果全部响应YES则进入下一个阶段。
PreCommit:协调者向所有参与者发送PreCommit命令,询问是否可以进行事务的预提交操作,参与者接收到PreCommit请求后,如参与者成功的执行了事务操作,则返回YES响应,进入最终Commit阶段。一旦参与者中有向协调者发送了NO相应,协调者向所有参与者发送abort请求,参与者接收abort命令执行事务的中断。
DoCommit:在前两个阶段中所有参与者的响应反馈均是YES后,协调者向参与者发送DoCommit命令正是提交事务,如协调者没有接收到参与者发送的ACK响应,会向所有参与者发送abort请求命令,执行事务的中断。