数据库事务要知足几个要求:ACIDjava
Atomic(原子性) 事务必须是原子的工做单元sql
Consistent(一致性) 事务完成时,必须使全部数据都保持一致状态数据库
Isolation(隔离性) 并发事务所作的修改必须和其余事务所作的修改是隔离的编程
Duration(持久性) 事务完成以后,对系统的影响是永久性的网络
Mysql里的事务处理过程架构
事物处理完删除undo文件,更新redo文件并发
拆分出来,每个模块是独立的工程,可以缩短开发周期回归测试app
在分布式系统中,每个机器节点虽然都能明确的知道本身执行的事务是成功仍是失败,可是却没法知道其余分布式节点的事务执行状况。所以,当一个事务要跨越多个分布式节点的时候(好比,下单流程,下单系统和库存系统可能就是分别部署在不一样的分布式节点中),为了保证该事务能够知足ACID,就要引入一个协调者(Cooradinator)。其余的节点被称为参与者(Participant)。协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务进行提交。框架
X/Open Distributed Transaction Processing Reference Model 异步
X/Open是一个组织机构,定义出的一套分布式事务标准, 定义了规范的API接口
2PC(two -phase-commit), 用来保证分布式事务的完整性
J2EE 遵循了X/open DTP规范,设计并实现了java里面的分布式事务编程接口规范-JTA
XA是X/Open DTP定义的中间件与数据库之间的接口规范。 XA接口函数由数据库厂商提供
AP application :触发分布式事物指令 (应用程序)
RM resouces manager 资源管理器。 通常表示数据库,必须实现XA定义的接口
TM transaction manager 事务管理器,负责协调和事务管理
各个AP节点执行事务操做,将undo和redo信息记录到事务日志中,尽可能把提交过程当中所消耗时间的操做和准备都提早完成后确保后续
事务提交的成功率
各个AP成功执行了事务操做,那么反馈给TM yes的response;若是AP没有成功执行事务,就反馈TM no的response
假设一个事务的提交过程总共须要30s, 其中prepare操做须要28(事务日志落地磁盘及各类io操做),而真正commit只须要2s
那么,commit阶段发生错误的几率和prepare相比, 2/28 (<10%) .只要第一个阶段成功,那么commit阶段出现失败的几率就很是小
大大增长了分布式事务的成功几率
阶段一:canCommit
询问,按照全部人的返回决定是否能够执行操做
阶段二:preCommit
预执行,若是超时认为执行失败
阶段三:doCommit
若是超时了,仍是会真正执行
相对于2PC,3PC主要解决的单点故障问题,并减小阻塞,由于一旦参与者没法及时收到来自协调者的信息以后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。可是这种机制也会致使数据一致性问题,由于,因为网络缘由,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时以后执行了commit操做。这样就和其余接到abort命令并执行回滚的参与者之间存在数据不一致的状况。
XA 就是 X/Open DTP 定义的事务管理器与资源管理器的接口规范(即接口函数),XA 接口函数由数据库厂商提供。
JTA是基于X/Open DTP模型开发的java transaction APi规范
经过2pc的方式去完成分布式事务,虽然经过这种方式可以达到预期的效果,可是咱们在现实中不多会用到2pc方式的提交的XA事务,有几个缘由
目前互联网领域里有几种流行的分布式解决方案,但都没有像以前所说的XA事务同样造成X/OpenDTP那样的工业规范,而是仅仅在具体的行业里得到较多的承认
这个方案就是把一个业务流程中须要在一个事务里执行的多个相关业务接口包装整合到一个事务中,好比咱们能够讲A/B/C整合为一个服务D来实现单一事务的业务流程服务
eBay在2008年公布了一个关于BASE准则提到一个分布式事务解决方案。eBay的方案实际上是一个最终一致性方案,它主要采用消息队列来辅助实现事务控制流程,方案的核心是将须要分布式处理的任务经过消息队列的方式来异步执行,若是事务失败,则能够发起人工重试的纠正流程。人工重试被更多的应用于支付场景,经过对帐系统对过后问题进行处理
好比一个很常见的场景:某个用户产生了一笔交易,那么须要在交易表中增长记录,同时须要修改用户表的金额(余额),因为这两个表属于不一样的远程服务,因此就会涉及到分布式事务与数据一致性的问题
user(id, name, amt_sold, amt_bought) transaction(xid, seller_id, buyer_id, amount) |
begin; INSERT INTO transaction VALUES(xid, $seller_id, $buyer_id, $amount); UPDATE user SET amt_sold = amt_sold + $amount WHERE id = $seller_id; <br />UPDATE user SET amt_bought = amt_bought + $amount WHERE id = $buyer_id; commit; |
---|---|
那么在这里可使用消息队列(MQ)来作
先启动一个事务,更新交易表(transaction)后,并不直接更新user表,而是将要对user表进行的更新插入到消息队列中。
目标系统收到该消息之后,启动本地事务去对用户表的余额作调整
伪代码
bool result=dao.update(); if(result){ mq.send(); }
根据上面的伪代码的实现方案,可能出现几种状况
对于上面几种状况,问题都不大。那么咱们分析下消费端的问题
对于第一个问题,如何保证消息不丢失
如今用的比较广泛的MQ都具备持久化消息的功能,若是消费者宕机或者消费失败,均可以执行重试机制
也就是说若是队列中的消息由于网络异常致使发送屡次的状况下,仍然须要保证消息被应用屡次与应用一次产生的效果是同样的
增长一个message_applied(msg_id)表,用来记录已经被成功应用的消息。在目标系统执行更新操做以前,先检测该消息是否已经被消费过,消费完成后经过本地事务控制来更新这个“消费表状态”,用来避免消息重复消费问题
上面这种方式是很是经典的实现,基本避免了分布式事务,实现了“最终一致性”。
各大知名的电商平台和互联网公司,几乎都是采用相似的设计思路来实现“最终一致性”的。这种方式适合的业务场景普遍,并且比较可靠。不过这种方式技术实现的难度比较大
任何一个服务操做都提供一个查询接口,用来向外部输出操做执行的状态。服务操做的使用方能够经过接口得知服务操做执行的状态,而后根据不一样状态作不一样的处理操做
为了可以实现查询,每一个服务操做都须要有惟一的流水号
衰减查询 5s 10s 20s
有了查询模式,咱们就可以得知操做所处的具体状态,若是整个操做处于不正常状态,咱们须要修正操做中的出现问题的子操做。也许是要从新执行,或者取消已完成的操做。经过修复使得整个分布式系统达到最终一致。这个过程就是补偿模式
根据发起形式又分为
DTS(Distributed Transaction Service)框架是由支付宝在X/OpenDTP模型的基础上改进的一个设计,定义了相似2PC的标准两阶段接口,业务系统只须要实现对应的接口就可使用DTS的事务功能。DTS最大的特色是放宽了数据库的强一致约束,保证了数据的最终一致性。
TRYING(SQL事务中的LOCK):
CONFIRMING(SQL事务中的COMMIT)
CONFIRMING 阶段
TRYING阶段执行成功并开始执行CONFIRMING阶段时,默认 CONFIRMING阶段是不会出错的。即:只要TRYING成功,CONFIRMING必定成功。 CANCELING 阶段主要是在业务执行错误,须要回滚的状态下执行的业务取消,预留资源释放。
以上全部的操做须要知足幂等性,幂等性的实现方式能够是:
一、经过惟一键值作处理,即每次调用的时候传入惟一键值,经过惟一键值判断业务是否被操做,若是已被操做,则再也不重复操做
二、经过状态机处理,给业务数据设置状态,经过业务状态判断是否须要重复执行
如何更通俗的理解TCC事务模型
支付系统接收到会员的支付请求后,须要扣减会员帐户余额、增长会员积分(暂时假设须要同步实现)增长商户帐户余额会员系统、商户系统、积分系统是独立的三个子系统,没法经过传统的事务方式进行处理。
TRYING阶段:咱们须要作的就是会员资金帐户的资金预留,即:冻结会员帐户的金额(订单金额)
CONFIRMING阶段:咱们须要作的就是会员积分帐户增长积分余额,商户帐户增长帐户余额
CANCELING阶段:若是confirming阶段出现问题,该阶段须要执行的就是解冻释放咱们扣减的会员余额
相信作过支付宝交易接口都知道,咱们通常会在支付宝的回调页面和接口里,解密参数,而后调用系统中更新交易状态相关的服务,将订单更新为付款成功。同时,只有当咱们回调页面中输出了success字样或者标识业务处理成功相应状态码时,支付宝才会中止回调请求。不然,支付宝会每间隔一段时间后,再向客户方发起回调请求,直到输出成功标识为止。
其实这就是一个很典型的补偿例子,跟一些MQ重试补偿机制很相似。