分布式事务

事务(Transaction)-一般是指要做的或所做的事情-由事务开始(begin transaction)事务结束(end transaction)之间执行的全体操作组成。

简单的讲就是-要么全部被执行-要么就全部失败。

分布式事务-自然就是运行在分布式系统中的事务-是由多个不同的机器上的事务组合而成的。同上-只有分布式系统中所有事务执行了才能是成功-否则失败。

事务的基本特征ACID:

  • 原子性(Atomicity)
  • - 一个事务是一个不可分割的工作单位-事务中包括的诸操作要么都做-要么都不做。
  • 一致性
  • - 指事务执行前和执行后-数据是完整的。
  • 隔离性
  • - 一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的-并发执行的各个事务之间不能互相干扰。
  • 持久性
  • - 也称为永久性-一个事务一旦提交-它对数据库中数据的改变就应该是永久性的保存下来了。

    ---

    分布式事务的目标:解决多个独立事务一致性的问题。

    我们遇到的问题:

    分布式事务:一个功能-横跨多个微服务-由于每个微服务不在一个库-没法用数据库事务来保证事务。

    网约车例子:乘客支付订单。支付系统中-支付表更新-订单系统-订单库 订单状态更新为已支付。

    订单-支付表-在不同的库-如何保证两个库之间的事务。

    支付操作:支付修改余额-修改订单状态。

    分布式事务解决方案

    二阶段提交协议

    基于XA协议的-采取强一致性-遵从ACID.

    2PC:(2阶段提交协议)-是基于XA/JTA规范。

    #### XA

    XA是由X/Open组织提出的分布式事务的架构(或者叫协议)。XA架构主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA接口是双向的系统接口-在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。也就是说-在基于XA的一个事务中-我们可以针对多个资源进行事务管理-例如一个系统访问多个数据库-或即访问数据库、又访问像消息中间件这样的资源。这样我们就能够实现在多个数据库和消息中间件直接实现全部提交、或全部取消的事务。XA规范不是java的规范-而是一种通用的规范。

    #### JTA

    JTA(Java Transaction API)-是J2EE的编程接口规范-它是XA协议的JAVA实现。它主要定义了:

    一个事务管理器的接口javax.transaction.TransactionManager-定义了有关事务的开始、提交、撤回等操作。 一个满足XA规范的资源定义接口javax.transaction.xa.XAResource-一种资源如果要支持JTA事务-就需要让它的资源实现该XAResource接口-并实现该接口定义的两阶段提交相关的接口。

    > 《二阶段提交协议》illustration

    #### 过程

    ``sh 1.请求阶段(commit-request phase-或称表决阶段-voting phase)在请求阶段-协调者将通知事务参与者准备提交或取消事务-然后进入表决过程。在表决过程中-参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。2.提交阶段(commit phase)在该阶段-协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务-否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行响应的操作。 `

    #### 缺点:

  • 单点故障:事务的发起、提交还是取消-均是由老大协调者管理的-只要协调者宕机-那就凉凉了。
  • 同步阻塞缺点:从上面介绍以及例子可看出-我们的参与系统中在没收到老大的真正提交还是取消事务指令的时候-就是锁定当前的资源-并不真正的做些事务相关操作-所以-整个分布式系统环境就是阻塞的。
  • 数据不一致缺点:就是说在老大协调者向小弟们发送真正提交事务的时候-部分网路故障-造成部分系统没收到真正的指令-那么就会出现部分提交部分没提交-因此-这就会导致数据的不一致。
  • #### 无法解决的问题

    当协调者出错-同时参与者也出错时-两阶段无法保证事务执行的完整性。 考虑协调者再发出commit消息之后宕机-而唯一接收到这条消息的参与者同时也宕机了。 那么即使有了新的协调者-这条事务的状态也是不确定的-没人知道事务是否被已经提交。知道的人已经被灭口了。

    三阶段提交协议

    采取强一致性-遵从ACID。

    在二阶段上增加了:超时和预提交机制。

    有这三个主阶段-canCommit、preCommit、doCommit这三个阶段

    > 《三阶段提交协议》illustration

    #### 流程

    `sh 1.CanCommit阶段3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求-参与者如果可以提交就返回Yes响应-否则返回No响应。2.PreCommit阶段Coordinator根据Cohort的反应情况来决定是否可以继续事务的PreCommit操作。根据响应情况-有以下两种可能。A.假如Coordinator从所有的Cohort获得的反馈都是Yes响应-那么就会进行事务的预执行:发送预提交请求。Coordinator向Cohort发送PreCommit请求-并进入Prepared阶段。事务预提交。Cohort(一群大兵)接收到PreCommit请求后-会执行事务操作-并将undo和redo信息记录到事务日志中。响应反馈。如果Cohort成功的执行了事务操作-则返回ACK响应-同时开始等待最终指令。B.假如有任何一个Cohort向Coordinator发送了No响应-或者等待超时之后-Coordinator都没有接到Cohort的响应-那么就中断事务:发送中断请求。Coordinator向所有Cohort发送abort请求。中断事务。Cohort收到来自Coordinator的abort请求之后(或超时之后-仍未收到Cohort的请求)-执行事务的中断。3.DoCommit阶段该阶段进行真正的事务提交-也可以分为以下两种情况:执行提交A.发送提交请求。Coordinator接收到Cohort发送的ACK响应-那么他将从预提交状态进入到提交状态。并向所有Cohort发送doCommit请求。B.事务提交。Cohort接收到doCommit请求之后-执行正式的事务提交。并在完成事务提交之后释放所有事务资源。C.响应反馈。事务提交完之后-向Coordinator发送ACK响应。D.完成事务。Coordinator接收到所有Cohort的ACK响应之后-完成事务。中断事务协调者没有接收到参与者发送的ACK响应-那么就执行中断事务。A.发送中断请求协调者向所有参与者发送abort请求B.事务回滚参与者接收到abort请求之后-利用其在阶段二记录的undo信息来执行事务的回滚操作-并在完成回滚之后释放所有的事务资源。C.反馈结果参与者完成事务回滚之后-向协调者发送ACK消息D.中断事务协调者接收到参与者反馈的ACK消息之后-执行事务的中断。 `

    #### 缺点

    如果进入PreCommit后-Coordinator发出的是abort请求-假设只有一个Cohort收到并进行了abort操作- 而其他对于系统状态未知的Cohort会根据3PC选择继续Commit-此时系统状态发生不一致性。

    #### 2和3 的区别

    加了询问-增大成功概率。

    对于协调者(Coordinator)和参与者(Cohort)都设置了超时机制(在2PC中-只有协调者拥有超时机制-即如果在一定时间内没有收到cohort的消息则默认失败)。协调者挂了-参与者等待超时后-默认提交事务。有一丢进步。

    如果参与者异常了-协调者也异常了-会造成其他参与者提交。

    在2PC的准备阶段和提交阶段之间-插入预提交阶段-使3PC拥有CanCommit、PreCommit、DoCommit三个阶段。 PreCommit是一个缓冲-保证了在最后提交阶段之前各参与节点的状态是一致的。

    基于消息的最终一致性形式

    采取最终一致性-遵从BASE理论。

    BASE:全称是-Basically Avaliable(基本可用)-Soft state(软状态)-Eventually consistent(最终一致性)三个短语的缩写-来自eBay的架构师提出。

  • Basically Avaliable:就是在分布式系统环境中-允许牺牲掉部分不影响主流程的功能的不可用-将其降级以确保核心服务的正常可用。
  • Soft state:就是指在事务中-我们允许系统存在中间状态-且并不影响我们这个系统。就拿数据库的主从复制来说-是完全允许复制的时候有延时的发生的。
  • Eventually consistent:还是以数据库主从复制为例说-虽然主从复制有小延迟-但是很快最终就数据保持一致了。
  • 分布式事务不可能100%解决-只能提高成功概率。两阶段之间时间-毫秒级别。

    补救措施:

    定时任务补偿。程序或脚本补偿。

    人工介入。

    TCC

    解决方案:TCC(Try、Confirm、Cancel)-两阶段补偿型方案。

    从名字可以看出-实现一个事务-需要定义三个API:预先占有资源-确认提交实际操作资源-取消占有=回滚。

    如果后两个环节执行一半失败了-记录日志-补偿处理-通知人工。

    `sh 2PC:是资源层面的分布式事务