一、数据库事务
四大特性: ACID
1、Atomic 原子性 事务的整个操作是一个整体, 不可分割,要么全部成功,要么全部失败
2、Consistency 一致性 指系统从一个正确的状态,迁移到另一个正确的状态
3、Isolation 隔离性 保证事务不受其他并发执行的事务的影响。
隔离性又分为四个级别:隔离级别介绍 https://www.cnblogs.com/EasyLive2006/p/7623863.html
读未提交(read uncommitted)、
读已提交(read committed,解决脏读)、
可重复读(repeatable read,解决虚读,数据不会被其他事务修改,但可插入数据)、
串行化(serializable,解决幻读)
4、Durability 持久性 数据一旦提交, 就永久的改变数据表数据
本地事务图解(mysql)
二、分布式事务
1、使用场景:一个方法内对多个资源管理器的操作
01、单模块中对多个数据库的操作(模块中对一个数据库的多个库操作也会涉及分布式事务,因为我们不同的库是使用不同的连接,同一个事务肯定是在一个连接上开启和提交的)
02、模块中调用其他服务对数据库的操作
分布式事务概述 http://www.tianshouzhi.com/api/tutorials/distributed_transaction/383
三、解决方案
方案一:基于XA的两阶段提交
阶段1:TM通知各个RM准备提交它们的事务分支。如果RM判断自己进行的工作可以被提交,那就就对工作内容进行持久化(此处持久化不是提交事务,比如mysql就是记录事务日志),再给TM肯定答复;要是发生了其他情况,那给TM的都是否定答复。在发送了否定答复并回滚了已经的工作后,RM就可以丢弃这个事务分支信息。
阶段2:TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。所有的RM都prepare成功就提交,否则回滚。
open group对DTP(分布式事务处理)主要有2个文档。DTP参考模型和DTP XA规范。
01、DTP参考模型最小实例
DTP模型:http://www.tianshouzhi.com/api/tutorials/distributed_transaction/383
02、DTP XA规范
XA规范主要定义了RM-TM的交互接口,比如事务开始、准备、提交、回滚等接口。XA规范的接口一些是事务管理器需要实现的,一些是资源管理器需要实现的。
JTA规范对XA的支持 http://www.tianshouzhi.com/api/tutorials/distributed_transaction/385
基于XA的两阶段提交说明
使用要求:RM支持XA规范
常用开源工具:atomikos http://www.tianshouzhi.com/api/tutorials/distributed_transaction/386
优点:改造工作量小
缺点:
1、同步阻塞问题(对性能影响较大,全程加锁)
2、单点故障。一旦协调者TM发生故障。参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。
3、数据不一致。在阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这会导致一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。
一般使用场景:单服务操作多数据库,对性能要求不高。
方案二:TCC两阶段补偿型
Try 阶段主要是对业务系统做检测及资源预留,完成业务的准备工作。
Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
TCC对比XA
优点:TCC把资源层面二阶段提交上提到了业务层面来实现,最终一致性,不会一直持有资源的锁。避免了XA两阶段提交占用资源锁时间过长导致的性能低下问题。
缺点:主业务服务和从业务服务都需要进行改造,从业务方需要改造成try、confirm、canel3个接口,开发成本高。
开源工具:tcc-transaction、ByteTCC、spring-cloud-rest-tcc
方案三:本地消息表 (异步确保一致)
执行流程:
1、A系统在本地事务里操作同时,插入一条数据到消息表;接着A系统将这个消息发送到MQ中去;
2、B系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不会重复处理消息;
3、B系统执行成功之后,就会更新自己本地消息表的状态以及A系统消息表的状态;
4、如果B系统处理失败了,那么就不会更新消息表状态,那么此时A系统会定时扫描自己的消息表,如果有未处理的消息,会再次发送到 MQ中去,让B再次处理;
本地消息表方案优缺点
本地消息表方案是国外的ebay搞出来的一套方案
要求:要在本地数据库新建一张本地消息表,然后我们必须还要一个MQ
优点:保证了最终一致性,哪怕 B 事务失败了,但是 A 会不断重发消息,直到 B 那边成功为止。
缺点:
1、严重依赖于数据库中与实际业务无关的消息表来管理事务
2、高并发的情况下难以扩展
3、要增加消息表来实现分布式事务,繁琐且工作量大。
4、对业务的侵入性太强了
方案四:基于RocketMQ的事务消息
执行流程:
1、先发送一个prepared消息到mq。接着,执行业务代码逻辑。然后,确认发送消息,这个时候,MQ将消息状态标记为可消费。
2、消费者消费MQ执行业务逻辑,发送消费成功信息
如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事务消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认。RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。或者发送确认消息失败直接回滚。
如果消费失败怎么办?可以重试或人工解决。
缺点:需要MQ支持事务消息,编码复杂
方案五:分布式事务中间件
开源中间件:
1、Seata:是阿里巴巴开源的分布式事务中间件,以高效并且对业务0侵入的方式解决分布式事务
http://seata.io/zh-cn/
2、TX-LCN:是个人开源的分布式事务中间件, https://github.com/codingapi/tx-lcn
下面主要介绍Seata
Seata中事务参与方:
TC:事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
TM:控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
RM:控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
一个典型的分布式事务过程:
1、TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
2、XID 在微服务调用链路的上下文中传播。
3、RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖。
4、TM 向 TC 发起针对 XID 的全局提交或回滚决议。
5、TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
详细介绍参考官方文档
https://github.com/seata/seata/wiki/%E6%A6%82%E8%A7%88
http://seata.io/zh-cn/
Seata优缺点
优点:
1、高性能和简单易用
2、提供了 AT、TCC、SAGA 事务模式,XA未来支持
3、多种模式兼容
缺点:
1、默认隔离级别是读未提交,但也支持读已提交
参考:
分布式事务概述 http://www.tianshouzhi.com/api/tutorials/distributed_transaction/383
分布式事务解决方案及实现 https://www.cnblogs.com/jing99/p/11769093.html
sqlserver隔离级别 https://www.cnblogs.com/EasyLive2006/p/7623863.html
https://github.com/seata/seata/wiki/%E6%A6%82%E8%A7%88
http://seata.io/zh-cn/