分布式事务产生的背景
database transcation
数据库事务要求 ACID
Atomic(原子性) 事务必须是原子的工作单元 通过事务日志实现
Consistent(一致性) 事务完成时,必须使所有的数据都保持一致状态 通过事务日志实现
lsolation(隔离性) 并发事务所做的修改必须和其他事务所做的修改是隔离的 通过锁实现
Duration(持久性)事务完成后,对系统的影响是永久性的 通过数据库中的事务日志实现
mysql的事务处理过程
1,记录redo(事务修改以后的数据)和undo(事务修改之前的数据) 的 log文件 ,确保日志在磁盘上的持久化
2,更新数据记录
3,提交事务,redo.log 数据写入commit 记录
4,删除 redo undo log文件
分布式事务
数据库分库分表
SOA化 交易平台 订单处理
X/OpenDTP 事务模型
x/open distributed transcation processing reference model -------x/open 分布式事务处理模型
x/open 是一个组织机构,定义出的一套分布式事务处理标准,定义了规范的api接口
2PC(2-phase-commit),用来保证分布式事务的完整性
JTA 是 j2EE 遵循x/open规范,设计并实现了 java里面的分布式事务编程接口规范
XA 是x/open DTP 定义的中间件与数据库之间的接口规范。XA接口函数由数据库厂商提供
x/openDTP 中的角色
AP application 服务节点 发起指令
RM resouces manager 资源管理器。一般表示数据库,实现了XA定义的接口 执行针对自己数据库的指令 ,也可以是其他资源管理器, 比如消息队列,文件系统
TM trancation manager 事务管理器,事务协调者 。事务管理器、事务协调者,负责接收来自用户程序 (AP)发起的 XA 事务指令,并调度和协调参与事务的所有 RM(数据库),确保事务正确完成
- 1. 参与分布式事务的应用程序(AP)先到 TM 上注册全局事务
- 2. 然后各个 AP 直接在相应的资源管理器(RM)上进行事务操作
- 3. 操作完成以后,各个 AP 反馈事务的处理结果给到 TM
- 4. TM 收到所有 AP 的反馈以后,通过数据库提供的 XA接口进行数据提交或者回 滚操作
2PC( two-phase-commit )
阶段一:提交事务请求(投票阶段)
1,TM 向所有的 AP 发送事务请求,询问是否可以执行事务的提交操作,并等到各个AP的响应
2,执行事务
各个AP节点执行事务操作,将undo和redo信息记录到事务日志中,尽量把提交过程中消耗时间的操作和准备 都提前完成后确保后续事务提交的成功率
3,各个AP向TM反馈事务询问的响应
各个AP成功执行了事务操作,反馈给TM yes的response 否则 反馈no的response.
阶段二:执行提交事务
a) 执行提交事务
假设一个事务的提交过程总共需要30s, 其中prepare操作需要28(事务日志落地磁盘及各种io操作),而真正commit只需要2s
那么,commit阶段发生错误的概率和prepare相比, 2/28 (<10%) .只要第一个阶段成功,那么commit阶段出现失败的概率就非常小
大大增加了分布式事务的成功概率
b)中断事务提交
2PC具有明显的优缺点:
优点主要体现在实现原理简单;
缺点比较多:
- 2PC的提交在执行过程中,所有参与事务操作的逻辑都处于阻塞状态,也就是说,各个参与者都在等待其他参与者响应,无法进行其他操作;
- 协调者是个单点,一旦出现问题,其他参与者将无法释放事务资源,也无法完成事务操作;
- 数据不一致。当执行事务提交过程中,如果协调者向所有参与者发送Commit请求后,发生局部网络异常或者协调者在尚未发送完Commit请求,即出现崩溃,最终导致只有部分参与者收到、执行请求。于是整个系统将会出现数据不一致的情形;
- 保守。2PC没有完善的容错机制,当参与者出现故障时,协调者无法快速得知这一失败,只能严格依赖超时设置来决定是否进一步的执行提交还是中断事务。
3PC(three phase commit)
阶段一:canCommit
阶段二:preCommit
阶段三:doCommit
增加了一部询问操作,解决数据一致性问题
针对阻塞问题 增加了超时机制。
(98条消息) 分布式事务_三阶段提交(3PC)协议_pseudonym_的博客-CSDN博客_三阶段提交
分布式事务的实现
JOTM (java open trancation mannager) 基于JTA
Atomikes
通过2pc的方式去完成分布式事务,虽然通过这种方式能够达到预期的效果,但是我们在现实中很少会用到2pc方式的提交的XA事务,有几个原因
- 互联网电商应用的快速发展,对事务和数据的绝对一致性要求并没有传统企业应用那么高
- XA事务的介入增加了TM中间件,使得系统复杂化
- XA事务的性能不高,因为TM要等待RM回应,所以为了确保事务尽量成功提交,等待超时的时间通常比较长,比如30s到几分钟,如果RM出现故障或者响应比较慢,则整个事务的性能严重下降
分布式事务一致性
在 java 中,分布式事务主要的规范是 JTA/XA . JTA 是 java 的事务管理器规范, JTA 全称为 Java Transaction API, JTA 定义了一组统一的事务编程的接口,基于 X/OpenDTP 规范设计的分布式事务编程接口规范。XA 是工业标准的 X/Open DTP 规范
基于 JTA 规范的第三方分布式事务框架有 Jotm 和 Atomikos
JOTM
JOTM (java open transaction manager)是 ObjectWeb 的一个开源 JTA 实现,提 供 JTA 分布式事务的功能
但是 JOTM 存在一个问题,在使用中不能自动 rollback,无论什么情况都 commit。
Atomikos
与 JOTM 相比,Atomikos 更加稳定,原本 Atomikos 是商业项目,后来开源。论坛 比较活跃,有问题可以随时解决
互联网的分布式事务解决方案
目前互联网领域里有几种流行的分布式解决方案,但都没有像之前所说的XA事务一样形成X/OpenDTP那样的工业规范,而是仅仅在具体的行业里获得较多的认可
1,通过业务接口整合 避免分布式事务()
这个方案就是把一个业务流程中需要在一个事务里执行的多个相关业务接口包装整合到一个事务中,比如我们可以讲A/B/C整合为一个服务D来实现单一事务的业务流程服务
2,最终一致性的解决方案 -ebuy(base 模式 基本可用 软状态 分区容错性)
订单
用户账户表
insert into order ...//新增订单记录
update account set ...//冻结账户余额
MQ解决
伪代码
boolean result=insertOrder();
if(result){
activeMQ.sender();
}
问题:
a)mq发送失败: 事务回滚,
b) 消息出队列以后,消费者对应的业务操作要执行成功。
如果执行失败,消息不能失效或者丢失。需要保证消息和业务操作一致
现在用的比较普遍的MQ都具有持久化消息的功能,如果消费者宕机或者消费失败,都可以执行重试机制
c) 消息接收端会不会接到重复的消息(保证消费端的幂等):
1:幂等校验 mq_reciver(key : unique key唯一索引) 同一条消息使用相同的key 则无法插入 mq_reciver
2: 日志表来判断,或者状态锁来判断
status=0 ---> status=1
int effectRow=update acc set status=1 where status=0; // 第二次执行effectRow=0 无效
d) 发送端如何确认 消息接收端 执行成功
1 查询模式
消息接收端 提供一个查询接口,返回当前操作是否成功的状态
消息发送端 衰减查询 (5s,10s,20s)
任何一个服务操作都提供一个查询接口,用来向外部输出操作执行的状态。服务操作的使用方可以通过接口得知服务操作执行的状态,然后根据不同状态做不同的处理操作
为了能够实现查询,每个服务操作都需要有唯一的流水号
2 补偿模式
有了查询模式,我们就能够得知操作所处的具体状态,如果整个操作处于不正常状态,我们需要修正操作中的出现问题的子操作。也许是要重新执行,或者取消已完成的操作。通过修复使得整个分布式系统达到最终一致。这个过程就是补偿模式
根据发起形式又分为
自动恢复:通过对发生失败操作的接口自动重试或者回滚已经完成的操作
通知运营:如果程序无法自动完成恢复,则通过运营人员手动进行补偿
通知技术:通过监控或者告警通知到技术人员,通过技术手段进行修复
X/OpenDTP模型的支付宝的DTS架构
DTS(Distributed Transaction Service)框架是由支付宝在X/OpenDTP模型的基础上改进的一个设计,定义了类似2PC的标准两阶段接口,业务系统只需要实现对应的接口就可以使用DTS的事务功能。DTS最大的特点是放宽了数据库的强一致约束,保证了数据的最终一致性。
具体的流程是
TCC分为三个阶段trying---confirming---canceling。每个阶段做不同的处理。 TRYING、CONFIRMING、CANCELIING大致可以理解为SQL事务中的LOCK、COMMIT、ROLLBACK trying 阶段主要是对业务系统做检测及资源预留 confirming 阶段主要是对业务系统做确认提交,TRYING阶段执行成功并开始执行CONFIRMING阶段时,默认 CONFIRMING阶段是不会出错的。即:只要TRYING成功,CONFIRMING一定成功。 CANCELING 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。 以上所有的操作需要满足幂等性,幂等性的实现方式可以是: 1、通过唯一键值做处理,即每次调用的时候传入唯一键值,通过唯一键值判断业务是否被操作,如果已被操作,则不再重复操作 2、通过状态机处理,给业务数据设置状态,通过业务状态判断是否需要重复执行 |
如何更通俗的理解TCC事务模型
支付系统接收到会员的支付请求后,需要扣减会员账户余额、增加会员积分(暂时假设需要同步实现)增加商户账户余额 会员系统、商户系统、积分系统是独立的三个子系统,无法通过传统的事务方式进行处理。 TRYING阶段:我们需要做的就是会员资金账户的资金预留,即:冻结会员账户的金额(订单金额) CONFIRMING阶段:我们需要做的就是会员积分账户增加积分余额,商户账户增加账户余额 CANCELING阶段:该阶段需要执行的就是解冻释放我们扣减的会员余额 |
开源的tcc框架
tcc-transaction
bytetcc
最大努力通知型
做过支付宝交易接口的同学都知道,我们一般会在支付宝的回调页面和接口里,解密参数,然后调用系统中更新交易状态相关的服务,将订单更新为付款成功。同时,只有当我们回调页面中输出了success字样或者标识业务处理成功相应状态码时,支付宝才会停止回调请求。否则,支付宝会每间隔一段时间后,再向客户方发起回调请求,直到输出成功标识为止。
其实这就是一个很典型的补偿例子,跟一些MQ重试补偿机制很类似。