分布式事务

发布于 2022-04-26  2.18k 次阅读


一,分布式事务概念

   事务指的就是一个操作单元,在这个操作单元中的所有操作最终的行为都要保持一致,要么所有操作都成功,要么所有操作都失败被回滚,简单的说事务就是提供一套原子性操作

一,本地事务

本地事务数据库本身提供的一套事务机制,在单服务单库下可以通过本地事务实现一系列的原子性操作

数据库事务的四大特征:

  • A:原子性(Atomicity),一个事务(transaction)中的所有事务,要么全部成功,要么全部失败
  • C:一致性(Consistency),在一个事务执行之前和执行之后数据库都必须处于一致性状态
  • I:隔离性(Isolation):在并发环境中,当不同的事务同时操作相同的数据时,事务之间互不影响
  • D:持久性(Durability):指的是只要事务成功结束,它对数据库所在的更新就必须永久保存

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

在具体的编码过程中使用Spring提供的@Transactional来控制本地事务

二,分布式事务

  分布式事务指的是事务的参与者(微服务),支持事务的服务器,资源服务器以及事务管理器分别位于分布式系统上的不同节点之上

分布式事务的场景:以订单和库存为例

1,单服务访问多数据库

2,多服务服务访问单数据库

3,多服务访问多数据库

二,分布式事务的解决方案

分布式事务解决方案

  1. 全局事务
  2. 可靠消息服务
  3. 最大努力通知
  4. TCC(有多种实现框架)
  5. Seatas事务框架(AT模式,TCC模式,Saga模式,XA模式)

一,可靠的消息服务

   基于可靠消息服务的方案是通过消息中间件保证上下游应用数据操作的一致性,假设有A和B两个系统,分别可以处理任务A和任务B,此时存在一个业务流程,需要将任务A和任务B在同一个事务中处理,就可以使用消息中间件来实现

第一步:消息由A服务投递到中间件

  1. 在服务A处理事务性任务A前,首先向消息中间件发送一条消息
  2. 消息中间件收到后将消息持久化,但并不投递,持久化成功后向A回复一个确认应答
  3. 服务A收到确认应答后,则可以开始处理任务A
  4. 任务A处理完成后,向消息中间件发送Commit或者Rollback请求,该请求发送完成后,对服务A来说它的事务过程就结束了
  5. 如果消息中间件收到Commit,则向B服务投递消息,如果收到Rollback则直接丢弃消息

特殊情况:如果消息中间件没有收到Commit和Rollback,那么就要依靠中间件的"超时询问机制"


超时询问机制:在RocketMQ有封装,但在RibbitMQ需要自己实现

   服务A除了实现正常的业务流程外,还需提供一个事务询问接口,供消息中间件调用,当消息中间件收到发布消息便开始计时,如果到了超时时间没有收到任何指令,就会主动调用服务A提供的事务询问接口询问该服务目前的状态,该接口会返回三种结果,中间件根据三种结果做出不同反应

  • 提交(Commit):将该消息投递给服务B
  • 回滚(Rollback):直接丢弃消息
  • 处理中:继续等待

第二步:消息由消息中间件投递到服务B

  消息中间件向下游系统投递完消息后便进入堵塞等待状态,下游系统便立即进行任务的处理,任务处理完成后便向消息中间件返回应答

  • 如果消息中间件收到确认应答便认为该事务处理完毕
  • 如果消息中间件在等待确认应答超时之后就会重新投递,直到下游消费者返回消费成功应答为止,一般消息中间件可以设置消息重试的次数和时间间隔,如果最终还是不能成功投递,则需要人工干预,这里之所以使用人工干预,而不是让A系统回滚,主要是考虑整个系统设计的复杂性问题

基于可靠消息服务的分布式事务,前半部分使用异步,注重性能,后半部分使用同步,注重开发成本

二,最大努力通知

   最大努力通知也被称为定期校对,其实是对可靠消息服务方案的进一步优化,它引入了本地消费表来记录错误消息,然后加入失败消息的定期校对功能,来进一步保证消息被下游服务消费到

第一步:消息由服务A投递到中间件

  1. 处理业务的同一事务中,向本地消息表写入一条记录
  2. 准备专门的消息发送者不断地发送本地消息表中的消息到消息中间件,如果发送失败则重试

第二步:消息由中间件投递到系统B

  1. 消息中间件收到消息后负责将该消息同步投递给相应的下游服务,并触发下游系统的任务执行
  2. 当下游系统处理成功,向消息中间件反馈确认应答,消息中间件使可以将该消息删除,从而该事务操作成功
  3. 对于投递失败的消息,利用重试机制进行重试,对应重试失败的消息,写入错误消息表
  4. 消息中间件需要提供失败消息的查询接口,下游服务会定期查询失败消息,并将其消费

优点:非常经典的一种实现方式,实现了最终一致性

缺点:消息表耦合在业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理

三,Seata框架

单独写一篇,Seata的TCC和AT等模式,都是不同的解决方式,但底层都基于了DTP模型(二阶段提交)


路漫漫其修远兮,吾将上下而求索