分布式事务概述

1,840 阅读5分钟

「这是我参与 2022 首次更文挑战的第 12 天,活动详情查看:2022 首次更文挑战

君子以独立不惧,遁世无闷。

前言

在单体项目中,我们可以使用 Spring 的管理器来管理事务,以实现业务需求。但在分布式项目开发中,没有办法再使用 Spring 的事务管理器来处理了,就需要使用分布式事务

分布式事务的解决方案

目前分布式事务的解决方案主要有一下几种:

  • 1 基于 XA 协议:两阶段提交和三阶段提交,即 2PC 和 3PC,需要数据库层面支持。
  • 2 基于事务补偿机制TCC(try-confirm-cancel),需要在业务层面进行实现。
  • 3 本地消息表:是本地数据库 + mq 的方式实现,事务维护进行中的状态在本地数据库,通过 mq 发送消息通知对方服务,完成后通过回调或者消息来修改状态为已完成。需要使用定时任务扫描任务状态表,消息重发从而保证幂等性,其实时性不高,在简单的场景中应用广泛。
  • 4 事务消息:基于 RocketMQ 或者 RabbitMQ 来实现。

两阶段和三阶段事务

两阶段提交

应用程序发起事务,交予事务管理器进行处理,事务管理器即是协调者,资源管理器或者说是数据库就是参与者。

阶段一: 准备阶段,发送 prepare 消息,每个参与者执行本地事务但不提交,进入 ready 状态,并通知协调者已经准备就绪。

阶段二:提交事务。发送 commit 消息,当协调者确认每个参与者都 ready 后,通知所有的参与者进行 commit ,如果有资源管理器返回 failure, 则发送 rollback 命令,各个参与者进行回滚。

两阶段提交的问题:

  • 1 单点故障:一旦事务管理器出现故障或者阻塞,就会到这整个系统的不可用。
  • 2 数据不一致: 在第二阶段,由于网络故障,导致事务管理器只发送了部分的 commit 消息,那么部分参与者收到了 commit 消息提交了事务,没有收到消息的参与者没有提交事务,就会导致数据的不一致性。
  • 3 耗时较长: 在事务开启后,协调者和参与者所占用的资源就会被锁定,只有提交事务或者回滚后才能释放。
  • 4 不确定性:当事务管理器发送 commit 消息后,只有一个参与者收到了 commit 消息,那么当事务管理器和参与者同时宕机后,重新启动后无法确认该条消息是否成功提交。

三阶段提交

三阶段提交主要是对 2 阶段提交做了优化,解决了两阶段提交的单点故障问题,但是其性能开销大和数据可能存在的不一致情况依然存在。

  • 阶段一 : 事务管理器发送 canCommit 消息,确认数据库环境是否正常。如果所有的参与者都回复 yes,则进入第二阶段,如果有一个返回 no 或者等待超时,则中断事务操作,并向所有的参与者发送取消 abort 消息。
  • 阶段二 : 事务管理器发送 preCommit 消息,完成 sql 语句的执行操作,但是事务并未提交。参与者会回复事务管理器 ACK 消息,等待协调者的下一步指令。
  • 阶段三 : 如果阶段二的所有参与者回复了确认的消息,那么事务管理器就会执行提交操作,发送 doCommit 消息,通知所有的参与者进行提交事务。 如果有一个参与者没有提交消息或者超时,那么事务管理器会中断事务,向所有参与者发送取消事务消息。

三阶段提交引入了超时机制,如果参与者执行 preCommit 消息成功,如果在一定时间内还未收到事务管理器发送的 doCommit 消息,那么会认为事务管理器宕机,会自动执行 doCommit 操作。

TCC 事务模型

TCC 事务补偿机制,针对每一个操作都要有对应的确认和补偿的操作。

try 命令知识做业务的检查和资源的准备工作, confirm 做业务的确认操作, cancel 取消操作则是执行 try 的相反操作即回滚操作。事务管理器首先对所有分支事务进行 try 操作,如何任何一个分支事务执行操作失败,那么将会对所有的分支事务进行回滚。如果所有的分支事务都操作成功,那么事务管理器将会对所有的分支事务进行确认。如果 confirm 或者 cancel 执行失败,那么事务管理器就会选择重试。

看了 TCC 事务模型的原理,我们就知道该模型对业务的侵入性太强,改造难度较大,每个操作都要有 try 、 confirm 、 cancel 三个接口实现,如果 confirm 或者 cancel 阶段出错,需要进行重试,这两个阶段的接口需要保证接口方法的幂等性,如果重试一定次数还是不能解决,就需要释放资源,等待人工介入进行干预。