分布式事务
基本概念
事务
由若干个操作组成的一个完整的逻辑过程
本地事务
A原子性:事务的所有操作要么全部成功要么全部失败
I 隔离性:多个事务间的执行互不干扰,有不同的隔离级别
D持久性:事务完成后对数据的变更会持久化到硬盘上
C一致性:事务完成后,数据的完整性没有被破坏;这是事务的目的,上面三者是实现方式
使用常见的关系型数据库可以保证本地事务的数据一致性
分布式事务
分布式事务主要有以下几种情况:
- 远程调用其他服务修改数据
- 跨数据库实例
- 多个jvm进程访问同一个数据库实例
关系型数据库能保证分布式事务的数据一致性?
不能
案例1:
begin;
1数据库操作 #成功
2远程调用 #成功
3数据库操作 #失败,回滚
commit;
#此时远程调用2并不会被回滚,数据发生了不一致
案例2:
begin;
1数据库操作 #成功
2远程调用 #成功,但网络问题未及时返回应答,回滚
commit;
# 此时操作2已经执行,也发生了数据不一致
解决方案
2PC方案
理论基础
2PC即两阶段提交协议,将整个事务流程分为准备阶段和提交阶段
事务过程由事务管理器和参与者组成,事务管理器负责决策整个分布式事务的提交和回滚,事务参与者负责自己本地事务的提交和回滚
两个阶段:
- 准备阶段:事务管理器向每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交;
- 提交阶段:如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚消息,否则发送提交消息;最后参与者各自执行本地事务的提交或回滚,并释放锁;
具体实现
2PC方案是基于关系型数据库实现的
- XA方案
- seta方案
TCC方案
理论基础
TCC是Try、Confirm、Cancel
三个词语的缩写,分别为业务的三个操作/阶段
Try:业务检查、预留资源(检查金额、冻结金额)
Confirm:确认提交阶段,Try阶段所有分之事务都成功即可执行Confirm(把冻结的金额归零),TCC情况下认为Confirm一定不会失败
Cancel:回滚阶段,Try阶段有操作失败时需要执行补偿操作(把冻结的金额放回余额中)
这种方案对业务代码的侵入性非常强,要编写很多额外的代码,可能还需要添加表的字段(冻结金额)
可靠消息最终一致性方案
基本概念
当事务的发起方执行事务成功后会发出一条消息,消费者一定能接收消息并处理事务成功,该方法强调的是本地事务执行成功就一定会发出消息!消费者也一定会消费该消息保证数据一致性(最终一致)!
此方案需要解决三个问题:
-
本地事务与消息发出的原子性
begin ; 1. 数据库操作 2. 发送MQ #超时回滚,但消息已经发出,不一致! commit;
-
事务参与方接收消息的可靠性(不漏消费)
-
消息重复消费,方法幂等
具体实现
本地消息表方案
新加一张消息日志表,定时扫描表中未发送的消息给MQ,消费端消费完之后修改日志状态
#使用数据库保证新增用户与消息日志的原子性
begin;
新增用户
新增对应日志
commit;
RocketMQ事务消息方案
RocketMQ 4.3之后实现了完整的事务消息,将本地消息表移动到MQ内部,解决Producer端的消息发送与本地事务执行的原子性问题;
关键流程:
- 发送半消息到MQ,此时消费者不可消费
- 本地事务执行完发送
commit
或rollback
消息,对应半消息变为可消费/被删除 - 半消息未收到commit时,会执行事务回查,检查事务是否提交
最大努力通知方案
系统A调用B,系统B会将结果通知给A
- 最大努力,也就是重试若干次,失败就不通知了
- 会提供一个接口,A调用该接口完成校验
参考资料:
Q.E.D.