国际开放标准组织定义了DTS(分布式事务处理模型),模型中包含4种角色:应用程序,事务管理器,资源管理器和通讯资源管理器。 事务管理器是统管全局的管理者,资源管理器,通讯资源管理器是事务的参与者。算法
在JEE中定义了TX协议和XA协议,TX协议定义了应用程序与事务管理器直接的接口,XA协议定义事务管理器定义与资源管理器直接的接口。数据库
下面咱们介绍的两阶段提交,三阶段提交以及阿里提出的TCC都是根据DTS这一思想演变而来。缓存
1.两阶段提交协议。并发
2.三阶段提交异步
3.TCC 分布式
前面提到的两阶段和三阶段提交协议,实际上他们能解决分布式事务问题。可是遇到极端状况是,系统会阻塞或者不一致的问题,须要运营或者技术人员解决。两阶段和三阶段中都有不少参与者、多个阶段实现一个事务,实现复杂,性能也是一个问题。在互联网高并发系统中,鲜有使用两阶段,三阶段的场景。高并发
后面有人提出了TCC协议,TCC就是将一个任务拆分红Try、Confirm、Cancel三个步骤。正常的流程会先执行Try,若是执行没有问题,则在执行Confirm,若是在执行过程当中出现了问题,则执行操做的逆操做Cancel。从正常的流程来说这也是一个两阶段提交,可是在执行出现问题的时候有必定的自我修复能力。若是任何参与者出现了问题,则协调者经过执行操做的逆操做来Cancle以前的操做。达到最终的一致性。性能
保持最终一致性的模式spa
在大规模、高并发服务化系统中,一个功能被拆分红多个具备单一子功能的服务,一个流程会有多个系统的多个单一功能的服务组合实现,若是使用两阶段或者三阶段提交协议,确实能解决系统间的一致性问题 。除了这两个协议的自身问题,其实现也比较复杂、成本比价高。性能也很差.相比较而言,tcc协议更简单而且更好实现。可是tcc协议因为每一个事务都要执行try在执行confirm,略显臃肿。所以,现实系统的底线是仅仅达到最重一致性。而不是实现专业的,复杂的一致性协议。实现最重一致性有一些很是有效,简单的模式。下面就介绍这些模式的及其应用场景。接口
1.查询模式
任何服务操做都提供一个查询接口,用来向外部输出操做执行的状态。服务操做的适用方可经过查询接口得知服务操做执行的状态。为了实现查询,每次服务操做都要提供惟一的流水号标识。
2.补偿模式
有了上面的查询模式,在任何状况下,咱们都能得知具体的操做所处的状态,若是操做处于不正常的状态。则须要处理操做中有问题的子操做。多是从新执行为完成的子操做或者取消已完成的子操做。经过修复使得分布式系统达到一致。
3.异步确保模式
异步确保模式是补偿模式的一个典型案例,常常应用到使用方响应时间要求不过高的场景中,一般把这些类操做从主类中摘除,经过异步到方式进行处理,处理后把结果经过通知系统通知给使用方。这样能够对高并发进行削峰。如电商系统里到物流,配送。
将要执行到异步操纵封装入库,而后经过定时任务捞取未完成到任务进行补偿操做来实现异步取保机制。
4.按期校对模式
系统在没有达到一致以前,系统见到状态是不一致到,甚至是混乱的。须要经过补偿操做来达到最终一致性。
在操做主流中的系统间执行校对操做,能够在过后异步批量的校对操做的状态。若是发现不一致的操做,则进行补偿,补偿操做与补偿模式中的补偿模式中的补偿操做是同样的。
另外,实现按期校对的一个关键就是分布式系统中须要一个自始至终惟一的id,生产全局id通常有两种方式。持久性:数据库自增或者sequence 。时间性:通常由机器号,业务号,时间,节点内自增id。好比雪花算法。
全局id能够将一个请求在分布式系统中流转路径聚合,而调用连链中的spanid能够将聚合的请求路径经过树形结构进行展现。可以快速定位问题,提升应急效率。
5.可靠消息模式
在分布式系统中,对于主流程中优先级比较低的操做,大多采用异步的方式执行。也就是前面提到的异步确保模型。为了充分的解耦,也因为专业的消息队列提供可伸缩,可分片,可持久化等功能,一般经过消息队列实现异步化。对于消息队列,咱们须要创建特殊的设施来保证可靠的消息发送及消息处理机的冥等性。
消息的可靠发送 消息发送以前将消息持久化到数据库,状态表示为等待发送,而后发送消息,消息更改成发送成功。定时任务捞取一段时间后仍是待发送到消息,并发送消息。
处理到冥等性:存在消息发送到重试,就会可能出现重复消息。
保证操做的冥等性 的经常使用方法以下:
1.使用数据库表的惟一键进行滤重,拒绝重复的请求
2.使用分布式表对请求进行滤重。
3.使用状态流转的方向性滤重,一般使用数据库的行级锁来实现。
4.根据业务的特色,操做自己就是冥等的,例如:删除一个资源,新增一个资源。
6.缓存一致性模型
在大规模,高并发系统中一个常见的核心需求就是亿级的读请求,显然关系型数据库并非解决高并发读的最佳方案。
1 若是性能要求不是特别高,尽可能使用分布式缓存。
2.写缓存数据时必定要完整。
3.使用缓存牺牲了强一致性,为了提升性能,数据库与缓存只须要保证弱一致性
4.读的顺序时先读缓存,后读数据库。写的顺序时先写数据库,后写缓存。