1.分布式事务概述
1.1 事务简介
1.1.1 本地事务
大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务(Local Transaction)。本地事务的ACID特性是数据库直接提供支持。本地事务应用架构如下所示:
在JDBC编程中,我们通过java.sql.Connection对象来开启、关闭或者提交事务。代码如下所示:
Connection conn = ... //获取数据库连接
conn.setAutoCommit(false); //开启事务
try{
//...执行增删改查sql
conn.commit(); //提交事务
}catch (Exception e) {
conn.rollback();//事务回滚
}finally{
conn.close();//关闭链接
}
1.1.2 分布式事务
当下互联网发展如火如荼,绝大部分公司都进行了数据库拆分和服务化(SOA)。在这种情况下,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,用需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据的操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。
1.1.2.1 分布式事务典型场景
跨库事务:
分库分表:
服务化(SOA)
1.2 分布式事务解决标准及规范
X/Open,即现在的open group,是一个独立的组织,主要负责制定各种行业技术标准。官网地址:http://www.opengroup.org/。下图展示了open group目前主要成员(官网截图):
就分布式事务处理(Distributed Transaction Processing,简称DTP)而言,X/Open主要提供了以下参考文档:
<<Distributed Transaction Processing: Reference Model>>
分布式事务处理参考模型,下载地址:http://pubs.opengroup.org/onlinepubs/9294999599/toc.pdf
<< Distributed Transaction Processing: The XA Specification>>
分布式事务处理XA规范,下载地址:http://pubs.opengroup.org/onlinepubs/009680699/toc.pdf
1.2.1 DTP模型
在<<Distributed Transaction Processing: Reference Model>>第3版中,规定了构成DTP模型的5个基本元素:
应用程序(Application Program ,简称AP):用于定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作。
资源管理器(Resource Manager,简称RM):如数据库、文件系统等,并提供访问资源的方式。
事务管理器(Transaction Manager ,简称TM):负责分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚等。
通信资源管理器(Communication Resource Manager,简称CRM):控制一个TM域(TM domain)内或者跨TM域的分布式应用之间的通信。
通信协议(Communication Protocol,简称CP):提供CRM提供的分布式应用节点之间的底层通信服务。
其中由于通信资源管理器(Communication Resource Manager)和通信协议(Communication Protocol)是一对好基友,从Communication Protocol的简称CP上就可以看出来,两个元素的关系不一般,因此有的文章在介绍DTP模型元素时,只提到了通信资源管理器...
1.2.2 XA规范
XA规范的最主要的作用就是定义了RM-TM的交互接口,下图更加清晰了演示了XA规范在DTP模型中发挥作用的位置,从图中可以看出来,XA仅仅出现在RM和TM的连线上。
XA规范除了定义的RM-TM交互的接口(XA Interface)之外,还对两阶段提交协议进行了优化。 一些读者可能会误认为两阶段提交协议是在XA规范中提出来的。事实上:
1、两阶段协议(two-phase commit)是在OSI TP标准中提出的;
2、在DTP参考模型(<<Distributed Transaction Processing: Reference Model>>)中,指定了全局事务的提交要使用two-phase commit协议;
3、而XA规范(<< Distributed Transaction Processing: The XA Specification>>)只是定义了两阶段提交协议中需要使用到的接口,也就是上述提到的RM-TM交互的接口,因为两阶段提交过程中的参与方,只有TM和RMs。
XA规范中定义的RM 和 TM交互的接口如下图所示:
1.2.3 两阶段提交协议(2PC)
两阶段提交协议(Two Phase Commit)不是在XA规范中提出,但是XA规范对其进行了优化。而从字面意思来理解,Two Phase Commit,就是将提交(commit)过程划分为2个阶段(Phase):
两阶段提交协议存在问题:
1、同步阻塞问题。
2、单点故障。
3、数据不一致。
三阶段提交协议(Three-phase commit) ----- https://en.wikipedia.org/wiki/Three-phase_commit_protocol
协调者、参与者都引入了超时机制;三阶段 CanCommit、PreCommit(其中一个超时或者执行失败,则TM发起中断)和doCommit
3PC :
优点 :引入超时机制,降低了协调者与参与者之间的阻塞范围;
缺点:在参与者接收到preCommit之后,如果出现网络分区,那么该参与者节点会继续执行事务的提交,而其他节点会执行中断事务,最终会造成数据的不一致。
1.3 BASE理论与柔性事务
1.3.1 经典分布式系统理论(CAP定理)
2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。Brewer认为在设计一个大规模的分布式系统时会遇到三个特性:一致性(consistency)、可用性(Availability)、分区容错(partition-tolerance),而一个分布式系统最多只能满足其中的2项。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。之后,CAP理论正式成为分布式计算领域的公认定理。
1. 一致性
一致性指“all nodes see the same data at the same time”,即更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致,不能存在中间状态。
关于一致性,如果的确能像上面描述的那样时刻保证客户端看到的数据都是一致的,那么称之为强一致性。如果允许存在中间状态,只要求经过一段时间后,数据最终是一致的,则称之为最终一致性。此外,如果允许存在部分数据不一致,那么就称之为弱一致性。
2. 可用性
可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。
3. 分区容错性
分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。
前面我们提到的X/Open XA 两阶段提交协议的分布式事务方案,强调的就是一致性;由于可用性较低,实际应用的并不多。
而基于BASE理论的柔性事务,强调的是可用性,目前大行其道,大部分互联网公司采可能会优先采用这种方案。
1.3.2 BASE理论
eBay的架构师Dan Pritchett源于对大规模分布式系统的实践总结,在ACM上发表文章提出BASE理论。文章链接:https://queue.acm.org/detail.cfm?id=1394128
BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)。
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。
1. 基本可用(Basically Available)
指分布式系统在出现不可预知故障的时候,允许损失部分可用性。
2. 软状态( Soft State)
指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性。
3. 最终一致( Eventual Consistency)
强调的是所有的数据更新操作,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
1.4 刚性事务和柔性事务
刚性事务--强一致性(全局事务,标准的分布式事务):即为传统事务,满足原子性、一致性、隔离性、持久性。
解决方案:两阶段提交协议(2PC)
柔性事务--最终一致性:满足基本可用(Basically Available)、柔性状态(Soft State)、最终一致性(Eventual Consistency)。
解决方案:TCC(两阶段型、补偿型)、最大努力通知、可靠消息最终一致
2. 柔性事务解决方案
2.1 TCC (两阶段型、补偿型)
TCC是Try-Confirm-Cancel的简称:
Try阶段:
完成所有业务检查(一致性),预留业务资源(准隔离性)
Confirm阶段:
确认执行业务操作,不做任何业务检查, 只使用Try阶段预留的业务资源。
Cancel阶段:
取消Try阶段预留的业务资源。
案例:
TCC两阶段提交 VS XA两阶段提交
TCC两阶段提交与XA两阶段提交的区别是:
XA是资源层面的分布式事务,强一致性,在两阶段提交的整个过程中,一直会持有资源的锁。
TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。
TCC事务模型的优缺点:
优点:XA两阶段提交资源层面的,而TCC实际上把资源层面二阶段提交上提到了业务层面来实现。有效了的避免了XA两阶段提交占用资源锁时间过长导致的性能低下问题。
缺点:主业务服务和从业务服务都需要进行改造,从业务方改造成本更高。还是航班预定案例,原来只需要提供一个购买接口,现在需要改造成try、confirm、canel3个接口,开发成本高。
改进:TXC分布式事务设计方案
2.2 最大努力通知
最大努力通知型( Best-effort delivery)是最简单的一种柔性事务,适用于一些最终一致性时间敏感度低的业务,且被动方处理结果 不影响主动方的处理结果。典型的使用场景:如银行通知、商户通知等。
最大努力通知型的实现方案,一般符合以下特点:
1、不可靠消息:业务活动主动方,在完成业务处理之后,向业务活动的被动方发送消息,直到通知N次后不再通知,允许消息丢失(不可靠消息)。
2、定期校对:业务活动的被动方,根据定时策略,向业务活动主动方查询(主动方提供查询接口),恢复丢失的业务消息。
案例:
2.3 可靠消息最终一致
通过消息中间件解耦,但有个问题,消息发送方同时要发送消息和本地业务操作,是先发消息还是先处理业务?
先发消息再操作db?? 两者不一致如何处理?例:消息发送成功,db操作失败,撤回来?
先操作db再发消息? 两者不一致如何处理?例: db操作成功,消息发送失败?重试?重试失败?
生产者:
1、从主动方应用的角度来分析
异常情况 | 可能的状态 | 一致性 |
---|---|---|
预发送消息失败 | 消息未进存储,业务操作未执行(可能的原因:主 动方应用、网络、消息中间件、消息存储) | 一致 |
预发送消息后,主动方应用没有收到返回消息存储结果 | (1)消息未进存储,业务操作未执行 | 一致 |
(2)消息已进存储(待确认),业务操作未执行 | 不一致 | |
收到消息存储成功的返回结果 但未执行业务操作就失败 | 消息已进存储(待确认),业务操作未执行 | 不一致 |
2、从消息中间件的角度来分析
异常情况 | 可能的状态 | 一致性 |
---|---|---|
消息中间件没有收到主动方 应用的业务操作处理结果 | (1)消息已进存储(待确认),业务操作未执行(或业务操作出错回滚了) | 不一致 |
(2)消息已进存储(待确认),业务操作成功 | 不一致 | |
消息中间件收到业务操作结果(成功/失败),但处理消息存储中的消息状态失败 | (1)消息已进存储(待确认),业务操作未执行(或 业务操作出错回滚了) | 不一致 |
(2)消息已进存储(待确认),业务操作成功 | 不一致 |
3、异常情况总结及处理
异常情况 | 一致性 | 异常处理方法 |
---|---|---|
消息未进存储,业务操作未执行 | 一致 |
|
消息已进存储(状态待确认),业务 操作未执行 | 不一致 | 确认业务操作结果,处理消息 (删除消息) |
消息已进存储(状态待确认),业务 操作成功 | 不一致 | 确认业务操作结果,处理消息 (更新消息状态,执行消息投递) |
消费者:
序号 | 原因 |
---|---|
1 | 被动方应用接收到消息,业务处理完成后应用出问题,消息 中间件不知道消息处理结果,会重新投递消息。 |
2 | 被动方应用接收到消息,业务处理完成后网络出问题,消息 中间件收不到消息处理结果,会重新投递消息。 |
3 | 被动方应用接收到消息,业务处理时间过长,消息中间件因 消息超时未确认,会再次投递消息。 |
4 | 被动方应用接收到消息,业务处理完成,消息中间件问题导致收不到消息处理结果,消息会重新投递。 |
5 | 被动方应用接收到消息,业务处理完成,消息中间件收到了消息处理结果,但由于消息存储故障导致消息没能成功确认, 消息会再次投递。 |
总结:消息消费过程中产生消息重复发送主要是因为消息接收者成功处理完消息后,消息中间件没能及时更新消息投递状态(也就是 消息没能及时ACK确认)导致的。
处理方法:对于未确认的消息,采用按规则重新投递的方式进行处理。
问题:消息的重复发送会导致业务处理接口出现重复调用的问题。
约束:被动方应用对于消息的业务处理要实现幂等。
2.3.1 基于MQ的事务消息
以下展示了RocketMQ的事务消息机制:
RocketMQ第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态。
如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事物消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认。RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
如果消费失败怎么办?阿里提供给我们的解决方法是:人工解决。
2.3.2 独立消息服务
并不是所有的mq都支持事务消息,如kafka。此时可以使用独立消息服务、或者本地事务表。
2.4 MT分布式事务方案设计
2.4.1 整体设计
全局事务(强一致性)方案:JTA/XA
柔性事务(最终一致性)方案:TCC、TXC/GTS、BED、可靠消息
没有一种方案,可以解决所有的业务场景。因此,抽象出一个通用的事务引擎,在这个因引擎上,逐步引进对各种分布式事务解决方案的支持。
引擎最主要的作用:提供一个通用的事务日志上报、存储、以及事务传播机制方案。
而具体到的每个事务解决方案,协调事务分支的提交、回滚流程都是不同的,各自实现。
2.4.2 事务引擎设计
去中心化,事务引擎的功能直接嵌入应用,不需要独立部署事务引擎。
序列化:事务日志的存储格式,需要支持多种序列化方式
事务日志:redo/undo log,可以提供多种实现:数据库、本地磁盘等
重试策略:重试策略配置方案
定时调度:执行重试策略
AOP:各种方案都需要对用户的方法做代理,提供一个基础的实现,各个具体的方案,在这个层面上做扩展。
统一事务管理平台:用户接入配置信息管理,监控告警、统计。
3 分布式事务在支付系统中的应