一、分布式事务的概念
1.1 什么是事务
事务就是多个原子操作的组合。在事务中,如果其中一个操作执行失败,那么剩下的操作都不再执行,而之前执行过的操作也需要回滚。
1.2 事务的特性
事务有原子性、一致性、隔离性、持久性四个特性,取英文名的首字母,简称为ACID特性。
1.原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
2.一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
3.隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
4.持久性(durability):持久性也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
1.3 什么是分布式事务
分布式事务,顾名思义就是包含对分布式系统中不同节点的操作的事务,可以理解为将对同一库事务的概念扩大到了对多个库的事务,目的是为了保证分布式系统中的数据一致性。
通俗来讲,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
分布式事务处理的关键是:必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务的决定必须产生统一的结果:全部提交或全部回滚 。
二、分布式事务解决方案
2.1 两阶段提交(2PC)
两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。
运行过程:
* 准备阶段:
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
* 提交阶段:
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
存在的问题:
* 同步阻塞:
所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
* 单点问题:
协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
* 数据不一致:
在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
* 容错机制差:
任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
2.2 TCC方案
TCC方案是对两阶段提交的一种改进,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:
* Try 阶段主要是对业务系统做检测及资源预留
* Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
* Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
存在的问题:
* 对应用的侵入性强:
业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作,应用侵入性较强,改造成本高。
* 实现难度较大:
需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm 和 cancel 接口必须实现幂等。
2.3 本地消息表
第一阶段:
事务开始
扣除 A 账号减少 100 元金额
向事件表插入一条记录
事务结束
第二阶段:
定时器定时查询事件表
向消息队列写入消息
第三阶段:
事务开始
向消息执行日志表增加一条记录(幂等性设计,避免消息重复执行)
向 B 账号增加 100 元金额
事务结束
优缺点:
优点:
一种非常经典的实现,避免了分布式事务,实现了最终一致性。
缺点:
消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
2.4 MQ 事务消息
针对 本地消息表 解决方案做出的改进,有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
优缺点:
优点:
实现了最终一致性,不需要依赖本地数据库事务。
缺点:
实现难度大,主流 MQ 不支持,RocketMQ 事务消息部分代码也未开源。