老大:来,你搞一搞分布式事务吧
我:……, 啥是事务?
我:先从实践学起吧
我不懂什么是事务
如果事务都不懂,就更不用说分布式事务了,于是我马上开始学习了。
事务是应用程序中一系列紧密的操作,所有操作必须胜利实现,否则在每个操作中所作的所有更改都会被吊销。
事务应该具备 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 个性。
换成比拟容易了解的话就是,就是一组操作比方增删改查四个操作要么都胜利,要么都失败,不存后果不统一的状态。
我不懂什么是分布式事务
终于弄明确什么是事务了,又来了分布式事务。为什么须要分布式事务呢?
事务更多指的是单机版、单数据库的概念。分布式事务 指事务的参与者、反对事务的服务器、资源服务器以及事务管理器别离位于不同的分布式系统的不同节点之上。
换成比拟容易了解的话,就是多个事务之间再放弃事务的个性,也就是多个事务之间保障后果的一致性。
XA 标准
有了分布式事务的场景,就会有解决该问题的形式标准,XA 标准就是解决分布式事务的标准,具体形容见维基百科解释:
XA 标准提供了一种重要思维:
1、引入全局事务的管制节点,事务的协调者
2、多个本地事务划分多阶段提交(也就是上面讲的 2PC,3PC)
我不懂分布式计划
有了标准就会有落地计划,上面介绍基于 XA 标准的几个实现协定。
首先介绍两阶段提交 (Two-phase Commit) 和三阶段提交(Three-phase Commit)
2PC(Two-phase Commit)
两阶段提交,顾名思义就是要分两步提交。
这里第一阶段称为筹备或者投票阶段。引入一个负责协调各个本地资源管理器的事务管理器,
本地资源管理器个别是由数据库实现,事务管理器在第一阶段的时候询问各个资源管理器是否都就绪,并执行完除提交事务外所有事件,而后把后果返回给事务协调者。
如果收到每个资源的回复都是 胜利,则在第二阶段提交事务,如果其中任意一个资源的回复是 失败, 则回滚事务。
这里的实现形式和咱们平时开黑玩游戏时差不多,当咱们组队时,队长会让大家筹备,让队员上完厕所吃饱饭,如果所有队员都筹备好,那就开始游戏,如果有任一一个队员没有吃饱,没有确认筹备好,就不会开始游戏。
然而这种协定也会存在一些问题,如下:
同步阻塞,这是 2PC 最大的问题,严格的 2PC 执行过程中,所有参加节点都是事务阻塞型的。当参与者占有公共资源时,其余第三方节点拜访公共资源不得不处于阻塞状态
解决方案:引入引入超时机制,如果长时间没有收到响应,执行特定的动作。
协调者单点故障,协调者在 2PC 中是最重要的角色,同时也意味着如果他出问题,整个过程就 GG 了
解决方案:单点故障的惯例计划就引入正本而后当主节点挂掉后,从新选主,就像组队游戏中,如果队员都筹备好后,队长长时间蹲厕所不开始游戏,游戏程序个别就会踢掉队长,其余组员切换成队长身份。
数据不统一,尽管解决了下面几个问题,然而因为分布式系统存在很多网络抖动和调用失败场景还是会有数据不统一的状况,上面分为协调者、参与者、网络等故障来详细分析一下:
1、协调者发送筹备命令前挂掉
这种相当于事务间接没有开始,没有啥太大影响
2、协调者发送筹备命令后挂掉
这种状况,如果参与者没有超时机制,就会造成资源锁定
3、协调者发送提交命令前挂掉
这种状况和上一种状况相似,也会造成资源锁定
4、协调者发送提交命令后挂掉
这种状况很可能是可能胜利执行分布式事务的,因为曾经到了提交阶段阐明其余参与者都曾经筹备好,如果失败就一直重试
5、协调者发送回滚命令前挂掉
这种状况和 2、3 是相似的,因为参与者收不到执行操作的命令,如果没有超时会始终阻塞并占据着资源
6、协调者发送回滚命令后挂掉
这种状况和 4 差不多,也是很大概率是可能胜利执行回滚事务的,如果没有胜利,因为曾经造成了决定,所以只能一直重试
7、协调者发送筹备命令后,局部参与者挂掉
这种状况协调者有超时机制,间接断定成失败,而后告诉所有参与者回滚
8、协调者发送筹备命令后挂掉,且局部参与者挂掉
这种状况从新选举协调者后,发现还在第一阶段,因为没有收到挂掉参与者的响应,所以断定失败,告诉其余参与者执行回滚
9、协调者发送提交或回滚命令后挂掉,且收到音讯的参与者挂掉
这种状况从新选举协调者后,没有收到音讯的参与者没有执行事务,然而协调者无奈确定收到音讯的参与者执行第二阶段的提交或回滚到底是否胜利,就会呈现事务不统一的状况
3PC(Three-phase Commit)
从下面介绍的相干内容也能够大体晓得 2PC 的毛病和解决形式,于是就有了上面的解决协定,三阶段提交
从百科能够看到 3PC 的引入次要就是为了解决下面咱们说的 2PC 的毛病,咋就能解决呢?
1、3PC 是非阻塞协定
好的,就是为了解决了资源占用问题,次要也就是引入了参与者超时机制
2、第一阶段与第二阶段之间插入了一个筹备阶段
解决了在两阶段提交中,参与者在投票之后,因为协调者产生解体或谬误,而导致参与者处于无奈通晓是否提交或者回滚的“不确定状态”,也就是为了保障最初提交阶段之前所有参加节点状态统一
3PC 把 2PC 第一阶段再次拆分为 2 个阶段,多了一个阶段其实就是在执行事务之前来确认参与者是否失常,避免个别参与者不失常的状况下,其余参与者都执行了事务锁定资源。
他的大略步骤其实能够依照参与者 4 个状态来划分
0、初始状态,此阶段事务发起者触发全局事务,参与者切换本地状态为开始状态,并把本人注册到协调者中。
1、可提交或状态期待,此阶段协调者发送命令到每个注册过去的参与者,让他们更改状态为可提交状态。
2、预提交状态,此阶段协调者收到参与者确认能够提交并进入状态,而后协调者向他们发送预提交音讯,参与者锁定资源,并更改状态为预提交状态。同时 协调者也进入预提交状态。
3、提交状态,此阶段协调者依据参与者预提交的后果执行提交或回滚操作,而后开释资源。
通过这种形式能够解决一些 2PC 状态不统一问题。JBoss 上大佬的总结:
大略意思是,通过引入预提交阶段,协调者可能确定参与者提交前的状态,同时参与者也可能推断其余参与者状态
协调者失常的状况下,能够依据参与者状态切换的后果来决定是执行还是回滚。多出的一个预提交阶段就是为了对立状态。
参与者如果没有收到协调者音讯,会默认执行提交,尽管可能会导致数据不统一。
协调者挂掉从新选举后,会依据参与者和原主节点状态确定是执行还是回滚。
新协调者来的时候发现自己是可提交状态并且参与者为可提交和回滚状态,阐明通过投票回滚的,此时新协调者执行回滚命令
新协调者来的时候发现自己是预提交并且参与者处于预提交和提交状态,那么表明曾经通过了所有参与者的确认了,所以此时执行的就是提交命令
能够看到 3PC 因为多引入了一个阶段,性能会比拟低,而且其实也没有解决数据一致性问题,多了一个阶段的成果也不能保障成果肯定要比 2PC 要好,所以个别还是很少用。
TCC(Try-Confirm-Cancel)
2PC/3PC 模式基于 反对本地 ACID 事务 的 关系型数据库:
- 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
- 二阶段 commit 行为:马上胜利完结,主动 异步批量清理回滚日志。
- 二阶段 rollback 行为:通过回滚日志,主动 生成弥补操作,实现数据回滚。
相应的,TCC 模式从业务层面解决,不依赖于底层数据资源的事务反对:
- 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
- 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
- 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
所谓 TCC 模式,是指反对把 自定义 的分支事务纳入到全局事务的治理中,能够不依赖本地数据库,当然实现上能够依赖,更多的场景还是两者联合。
TCC 更多的是让业务来实现两阶段提交的思维,对业务侵入性大
Try 阶段定义为执行资源的锁定,这个阶段我认为比拟难实现,惯例的思路是
转账场景时可能须要把尝试账户余额是否足够,而后减去转账金额并把金额存入到长期字段,做到锁定金额
缓存场景可能就须要应用分布式锁,锁定住要操作的缓存值,或者取出某个缓存到另一个缓存
上传下载场景可能须要把文件存到服务器长期目录
Confirm 阶段定义为执行 try 阶段锁定的资源,也就是说基于 try 的胜利,能够持续操作,比方执行真正的转账、缓存操作、上传下载等。
Cancel 阶段定义为开释 Try 预留的资源,也就是说因为 Try 的失败,须要作出相应的弥补操作或者复原环境,比方删除掉转账时的长期字段、开释掉锁、清理临时文件等。
TCC 模式实现难度还是蛮大的,须要思考很多异样场景,还要思考资源如何锁定和开释,然而因为不会阻塞资源,利用方面也更广,据说还是有很多公司热衷于这种弥补型的事务实现形式
还有就是这里所说的 TCC 更多是一种思维,理论实现可能还是须要依据具体业务来做相应的调整,办法是死的,人是活的。
SAGA
实践根底(点击查看原论文):Hector & Kenneth 发表论文 Sagas(1987)
Saga 模式提供的是长事务解决方案,在 Saga 模式中,业务流程中每个参与者都提交本地事务,当呈现某一个参与者失败则弥补后面曾经胜利的参与者,一阶段正向服务和二阶段弥补服务都由业务开发实现。
实用场景:
- 业务流程长、业务流程多
- 参与者蕴含其它公司或遗留零碎服务,无奈提供 TCC 模式要求的三个接口
Saga 次要思维是依赖于状态机转换,长事务拆分成多个短事务,顺次执行短事务
如果某个短事务失败,则依照后面执行程序的逆序执行弥补事务
这种模式还少应用的,实现也是比较复杂,同时流程很长,当遇到相似场景时还是须要认真思考是否有必要去实现分布式事务呢?
本地音讯表
执行业务的时候 将业务的执行和将音讯放入音讯表中的操作放在同一个事务中,这样就能保障音讯放入本地表中业务必定是执行胜利的。
而后再去调用下一个服务,如果胜利了,音讯表的音讯状态能够间接改成已胜利。
如果调用失败,会有 后台任务定时去读取本地音讯表,筛选出还未胜利的音讯再调用对应的服务,服务更新胜利了再变更音讯的状态。
个别也会有重试次数限度,超出后执行回滚或者告诉人工染指。
可见本地音讯表也会呈现数据不统一的状况,尽量保障最终一致性。
音讯队列
此计划的意思是通过反对事务的音讯队列来实现分布式事务。
次要流程:
- 生产者发送半事务音讯到 MQ
- 生产者收到 MQ 胜利接管到之后,去执行本地事务,然而事务还没有提交。
- 生产者会依据事务的执行后果来决定发送提交或者回滚到音讯
- 生产者须要提供一个 查问事务状态接口,如果一段时间内半音讯没有收到任何操作申请,那么 MQ 会通过查问接口取得发送方事务执行后果。
- 如果是失败后果的音讯,MQ 间接抛弃,也就不会影响到消费者
- 如果是胜利后果的音讯,消费者生产半事务音讯,而后再去生产一般音讯
该计划与本地音讯不同点是去掉了本地音讯表,本地事务和 MQ 事务绑定在一起。目前市面上实现该计划的只有阿里的 RocketMq
最大致力告诉
这种形式请进行 最大致力 自行学习吧
我不懂怎么实现
学了这么多计划,本人实现还是很有难度。
常见的解决方案的实现框架有:byteTCC、华为 ServiceComb 实现的 DTM(华为 cloud 官网可见)、阿里 seata(免费版为 GTS)、腾讯 DTF
目前开源最火的还是 seata,反对模式多、官网文档具体,这里就不一一介绍了
对于 seata 的文章十分多,下篇文章也打算以 seata 框架实际分布式事务。
那 seata 是不是就完满了呢?当然不是,当前可能改良的几点
1、不反对控制台,没有可视化界面,验证全靠打印和连贯数据库
2、seata-server 高可用不反对 Raft 协定,事务信息齐全依赖于 DB、redis 等
3、undoLog 占用空间过大尤其是前后置镜像一个大 JSON 字段,数据量大时可能会入库慢,可能须要进行压缩
4、只能通过异样回滚,不反对相似 Spring 的 Rollback-Only 标记位回滚
5、全局锁的粒度是不是有点大,分支事务是否有必要上报状态到 TC
找到一份seata 开源作者 jimin slievrly 的分享视频一起学习
如果须要视频中 PPT 学习,公众号内回复 seata 即可:
我懂了
本文依照齐全没接触过事务的学习流程进行书写,脑图如下:
右边是根底,左边是计划,如果你也在学习分布式事务相干常识,能够参考。
本文是系列第一篇,前面打算 一篇为 seata 实战 ,一篇为seata 原理 和如何设 计一个通用分布式事务框架。感激浏览,欢送关注。