分布式事务解决方案

分布式事务解决方案

起男 1,315 2021-08-08

分布式事务

事务的定义

事务可以看做是一次大的活动,它由不同的小活动组成,这些小活动要么全部成功,要么全部失败

本地事务

在计算机系统中,更多的是通过关系型数据库来控制事务,这是利用数据库本身的事务特性来实现的,因此,叫数据库事务。而数据库通常和应用在同一个服务器,所以基于关系型数据库的事务又被称为本地事务

数据库事务在实现时会将一次事务涉及的所有操作全部纳入到一个不可分割的执行单元,该执行单元中的所有操作要么都成功,要么都失败,只有其中任一操作执行失败,都将导致整个事务的回滚

分布式事务

分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称为分布式事务

CAP理论

cap是consistency、availability、partition tolerance三个单词的缩写,分别表示一致性、可用性、分区容忍性

一致性

写操作后的读操作可以读取到最新的数据状态,当数据分布在多个节点上,从任意节点读取到的数据都是最新的状态

分布式系统一致性特点:

  • 由于存在数据同步的过程,写操作的响应会有一定的延迟
  • 为了保证数据一致性会对资源暂时锁定,待数据同步完成释放锁定资源
  • 如果请求数据同步失败则会返回错误信息,一定不会返回旧数据

可用性

任何事务操作都可以得到响应结果,且不会出现响应超时或响应错误

分区容忍性

通常分布式系统的各个节点部署在不同的子网,这就是网络分区,不可避免的会出现由于网络问题而导致的节点之间通信失败,此时仍可对外提供服务,这就叫分区容忍性

分区容忍性是分布式系统的基础能力

组合

所有分布式系统场景中不会同时具备cap三个特性,因为在具备了p的前提下c和a是不能共存的

BASE理论

强一致性和最终一致性

cap中的一致性要求在任何时间查询每个节点数据必须一致,它强调的是强一致性,但最终一致性是允许可以在一段时间内每个节点的数据不一致,但是经过一段时间每个节点的数据必须一致,它强调的是最终数据的一致性

base是basically available(基本可用)、soft state(软状态)、eventually consistent(最终一致性)的缩写

base理论是对cap中ap的一个扩展,通过牺牲一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终到达一致状态。满足base理论的事务,我们称之为“柔性事务”

  • 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用
  • 软状态:由于不要求强一致性,所以base允许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性
  • 最终一致:最终一致是指经过一段时间后,所有节点数据都将达到一致

2PC

2pc即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(prepare phase)、提交阶段(commit phase),2是指两个阶段,p是指准备阶段,c是指提交阶段

整个事务由事务管理器和参与者组成,事务管理器负责决策整个分布式事务的提交和回滚,事务参与者负责自己本地事务的提交和回滚

在计算机中部分关系型数据库如oracle、mysql等都支持两阶段协议

  • 准备阶段:事务管理器给每个参与者与发送者发送prpare消息,每个数据库与参与者在本地执行事务,并写入undo/redo日志,此时事务没有提交

    undo日志是纪录修改前的数据,用于数据库回滚

    redo日志是纪录修改后的数据,用于提交事务后写入数据

  • 提交阶段:如果事务管理器受到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚消息;否则,发送提交消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源

    必须在最后阶段释放锁资源

XA方案

2pc的传统方案是在数据库层面实现的,如oracle、mysql都支持2pc协议,为了统一标准减少行业不必要的对接成本,需要定制标准化的处理模型以及接口标准,国际开放组织open group定义了分布式处理模型dtp

dtp角色

  • ap(application program):应用程序,可用理解为使用dtp分布式事务的程序

  • rm(resource):资源管理器,可用理解为事务的参与者,一般情况下一个数据库实例,通过资源管理器对该数据库进行控制,资源管理器控制着分支事务

  • tm(transaction manager):事务管理器,负责协调和管理事务,事务管理器控制着全局事务,管理事务生命周期,并协调各个rm

    全局事务是指分布式环境中,需要操作多个数据库共同完成一个工作,这个工作即是一个全局事务

  • xa:dtp模型定义tm和rm之间通讯的接口规范叫做xa,简单理解为数据库提供2pc接口协议,基于数据库的xa协议来实现2pc又称xa方案

交互方式

  • tm向ap提供应用程序编程接口,ap通过tm提交及回滚事务
  • tm交易中间件通过xa接口来通知rm事务的开始、结束以及提交、回滚等

执行流程

  1. ap持有多个rm
  2. 应用程序通过tm通知各个rm进行准备阶段的工作,执行实际的业务操作,但不提交事务,资源锁定
  3. tm收到执行回复
    • 只要有一方失败则向其它rm发起事务回滚,回滚完毕,资源释放
    • 全部成功,此时向所有rm发起提交事务,提交完毕,释放资源

缺点

  • 需要本地数据库支持xa协议
  • 资源锁需要等到两个阶段结束,才会释放,性能不是很好

seata方案

seata是由阿里中间件团队发起的开源项目fescar,后更名seata,它是一个开源的分布式事务框架。传统2pc的问题在seata中得到了解决,它通过对本地关系型数据库的分支事务的协调驱动来完成全局事务,是工作在应用层的中间件,不需要数据库支持xa协议。主要优点是性能较好,且不长时间占用连接资源,它以高效并且对业务零侵入的方式解决微服务场景下的分布式事务问题,它目前提供at模式(2pc)及tcc模式的分布式事务解决方案

设计思想

seata的设计目标其一是对业务的无侵入,因此从业务无侵入的2pc方案着手,在传统2pc的基础上演进,并解决2pc方案面临的问题

seata把一个分布式事务理解成一个包含了若干分支事务的全局事务

  • 全局事务的职责是协调旗下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚
  • 分支事务通常本身就是一个关系型数据库的本地事务

组件

  • tc(transaction coordinator):事务协调器,它是独立的中间件,需要独立部署运行,它维护全局事务的运行状态,接收tm指令发起全局事务的提交与回滚,负责与rm通信协调各个分支事务的提交和回滚
  • tm(transaction manage):事务管理器,tm需要嵌入应用程序中工作,它负责开启一个全局事务,并最终向tc发起全局提交或全局回滚命令
  • rm(resource manager):控制分支事务,负责分支注册、状态汇报,并接收tc的指令

执行流程

  1. tm和rm向tc进行注册
  2. 当tm执行到@GlobalTransactional时开启全局事务,并向tc发送消息,然后返回xid
  3. rm向tc注册分支事务,用xid和全局事务绑定,然后返回branchId
  4. 写入业务数据,写入undolog,提交分支事务
  5. 上报分支事务处理结果
  6. tm发起提交或回滚全局事务
  7. 成功:tc告知rm提交分支事务,删除undo_log;失败:tc告知rm回滚事务,解析undo_log,执行反向操作,删除undo_log,提交本地事务

每个rm使用DataSourceProxy连接数据库,其目的是使用ConnectionProxy,使用数据源和数据连接代理的目的就是在第一阶段将undo_log和业务数据放在一个本地事务提交,这样就保证了只要由业务操作就一定有undo_log

在第一阶段undo_log中存放了数据修改前和修改后的指,为事务回滚做好准备,所以第一阶段完成就已经将分支提交,也释放了锁资源

tm开启全局事务开始,将xid全局事务id放在事务上下文中,通过feign调用也将xid传入下游分支事务,每个分支事务将自己的branchid分支事务id与xid关联

第二阶段全局事务提交,tc会通知各个分支参与者提交分支事务,在第一阶段就已经提交了分支事务,这里各个参与者只需要删除undo_log即可,并且可用异步执行,第二阶段很快可以完成

第二阶段全局事务回滚,tc会通知各个分支参与者回滚分支事务,通过xid和branchid找到相应的回滚日志,通过回滚日志生成反向sql并执行,以完成分支事务回滚到之前的状态,如果回滚失败则会重试回滚操作

和传统2pc区别

  • 机构层次方面,传统2pc的rm实际上是在数据库层,rm本质上就是数据库自身,通过xa协议实现,而seata的rm是以jar包的形式作为中间件层部署在应用程序的
  • 两阶段提交方面,传统2pc无论第二阶段的决议是提交还是回滚,事务性资源的锁都要保持到阶段二完成才释放。而seata的做法是在阶段一就将本地事务提交,这样可用省去持锁时间

TCC

tcc是try confirm cancel的缩写,tcc要求每个分支事务实现三个操作:预处理try、确认confirm、撤销cancel。try操作做业务检查及资源预留,confirm做业务确认操作,cancel实现一个与try相反的操作即回滚操作。tm首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,tm将发起所有事务的cancel操作,若try操作全部成功,tm将发起所有分支事务的confirm操作,其中confirm/cancel操作若执行失败,tm会进行重试

三个阶段:

  1. try:做业务检查(一致性)及资源预留(隔离)
  2. confirm:确认提交,try阶段所有分支事务执行成功后开始执行confirm
  3. cancel:在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放

异常处理

  • 空回滚:在没有调用try方法的情况下,调用了第二阶段的cancel方法,cancel方法需要识别出这是一个空回滚,然后之间返回成功。针对cancel
  • 幂等:为了保证不会发生数据不一致,要求try、confirm和cancel接口保持幂等性,这样不会重复使用或释放资源
  • 悬挂:指cancel接口比try接口先执行,要求如果cancel执行了try就不要执行了。针对try

和2pc的区别

2pc通常都是在跨库的db层面,而tcc则在应用层面的处理,需要通过业务逻辑来实现

优势

  • 可以让应用自己定义数据操作的粒度,使得降低锁冲突,提高吞吐量成为可能

缺点

  • 对应用的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作
  • 实现难度大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略

可靠消息最终一致性

可靠消息最终一致性方案是指当事务发起方执行完本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发送给事务参与方最终事务就要达到一致

常见问题

  • 本地事务和消息发送的原子性问题:事务发起方在本地事务执行成功后消息必须发出去,否则就丢弃消息。即实现本地事务和消息发送的原子性,要么都成功,要么都失败。本地事务与消息发送的原子性问题是实现可靠消息最终一致性方案的关键问题
  • 事务参与方接收消息的可靠性:事务参与方必须能够从消息队列收到消息,如果接收失败可以重复接收消息
  • 消息重复消费问题:由于网络2的存在,若某一个消费节点超时但是消费成功,此时消息中间件会重复投递此消息,就导致了重复消费,需要实现事务参与方的方法幂等性

最大努力通知

目标:发起通知方通过一定的机制最大努力将业务处理结果通知到接收方

具体包括

  • 有一定的消息重复通知机制:因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知
  • 消息校对机制:如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息来满足需求

和可靠消息一致性的区别

  • 解决方案思路不同

    可靠消息一致性:发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证

    最大努力通知:发起通知方尽最大努力将业务处理结果通知给接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方

  • 两者业务应用场景不同

    可靠消息一致性:关注的是交易过程中的事务一致,以异步的方式完成操作

    最大努力通知:关注的是交易后的通知事务,即操作结果可靠的通知出去

  • 技术解决方向不同

    可靠消息一致性:要解决消息同发出到接收的一致性,即消息发出并且被接收到

    最大努力通知:无法保证消息从发送到接收的一致性,只能提供消息接收的可靠性机制。可靠性机制是,最大努力中将消息通知给接收方,当消息无法被接收方接收时,由接收方主动查询消息

最大努力通知方案是分布式事务中对一致性要求最低的一种,适用于一些最终一致性时间敏感度低的业务