1.单点事务
1.1.概述
单点事务,即本地事务,即传统的单机数据库事务,是一系列约定规则,约定了事务具备ACID原则:
- 原子性(A)
- 一致性(C)
- 隔离性(I)
- 持久性(D)
- 1.原子性
在整个事务中的所有操作,要么全完成,要么全不做,没有中间状态。在执行过程中发生的错误,所有的操作都会被回滚,整个事务就像没有被执行过一样。
2.一致性
事务的执行必须保证系统的一致性,在事务开始前和事务结束后,数据库的完整性没有遭到破坏,以转账为例,A有500元,B有500元,如果在一个事务里A成功转给B50,那么不管发生什么最终这个事务执行完后必须是A有450,B有550。也就是说只会有前后两个状态,不能存在其他中间状态。
3.隔离性
事务与事务之间互补影响,一个事务的中间状态不会被其他事务感知,数据库保证隔离性有四种隔离级别:
Read Uncommitted(读未提交内容)
Read Committed(读提交内容)
Repeatable Read(可重读)
Serializable(可串化)
4.持久性
一旦事务提交了,那么事务对数据所做的变更就完全保存在了数据库中,断电、宕机均不丢失。
1.2.实现原理
数据库的事务实现依靠日志,数据库中的文件分两种,数据文件和日志文件。日志文件既有缓存在log buffer中的,也有存在磁盘中的。日志文件中有两种与事务有关,undo日志和redo日志。
1.2.1.undo日志
Undo日志用于保证数据库具有原子性(Atomicity),如果事务执行失败,回滚数据时就要用到undo日志。
undo log的原理很简单,为了满足事务的原子性,在操作任何数据前,首先都会将数据备份到undo log,然后再进行数据的修改,如果出现错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中备份的数据恢复到事务开始之前的状态。数据库写入数据到磁盘之前,会把数据先缓存在内存中,事务提交时才会写入磁盘中。
情景示例:
1.事务开始
2.记录A=1到undo log
3.修改A=3
4.记录B=2到undo log
5.修改B=4
6.将undo log写到磁盘
7.将数据写到磁盘
8.事务提交
如何保证持久性:
事务提交前把数据写到磁盘。
如何保证原子性:
有undo log在,出现错误可以回滚。
缺陷:
如果每个事务提交前都将数据和Undo log写入磁盘,会导致大量磁盘IO,因此性能很低。
如果能够将数据缓存一段时间,就能减少磁盘IO,但是这样会丧失事务的持久性,因此引入另一种机制来实现持久化,即redo log。
1.2.2.redo日志
redo log记录了对数据的新操作,持久化redo log就能达到持久化数据的作用。直接进行数据的IO,IO的是一张数据页,redo log中只是记录了数据操作,比直接进行数据页的IO,效率要高得多。因此数据库真正进行事务提交的过程为:
情景示例:
事务开始
记录A=1到undo log buffer
修改A=3
记录A=3到redo log buffer
记录B=2到undo log buffer
修改B=4
记录B=4到redo log buffer
将undo log写入磁盘
将redo log写入磁盘
事务提交
使用redo log进行持久化,事务提交后,内存中的数据一定是具有一致性的,磁盘上的数据会找个时间异步将redo log中的操作同步到磁盘上一保证磁盘上的数据和内存中的数据具有一致性。即使在将redo log的数据写入磁盘前断电了,重启后数据库也会去读redo log来保证数据一致性的。
2.分布式事务
2.1.产生原因
分布式事务是指在在分布式架构下一个事务中的不同子操作是在不同机器上执行的,子操作之间状态无法相互感知,其中有子操作出错后其他机器上的子操作无法正确回滚。
分布式事务产生的根本原因:各个服务之间无感知
举一个例子:
一个下单事务,包含创建订单,库存服务,需要顺序调用订单服务、库存服务。如果库存服务挂掉后,订单服务也是无法回滚的,因为订单服务感知不到库存服务是否成功,即无法感知到库存服务的状态。
2.2.解决方法
2.2.1.DTP模型
1994年,X/Open组织(即现在的Open Group)定义了分布式事务处理的DTP模型,该模型包括几个角色:
- 应用程序(AP)
服务
事务管理器(TM)
全局事务管理者
资源管理器(RM)
一般是数据库
通信资源管理器(CRM)
TM和RM之间的通信中间件
DTP模型中,一个分布式事务(全局事务)可以被拆分成多个本地事务,运行在不同的AP和和RM上。每个本地事务的ACID由自身保证,全局事务必须保证每个本地事务必须同时成功,若有一个失败其他事务必须回滚。由于本地事务之间相互是无法感知状态的,因此要通过CRM来通知各个本地事务,同步事务执行的状态。
由于各个本地事务之间需要通信,通信就需要标准,因此配套提出了XA通信标准。XA规定了DTP中CRM和TM之间的通信的接口规范,定义了用于通知事务开始、提交、终止、回滚等接口,各个数据库厂商之间必须实现该接口。
2.2.2.分阶段提交
分阶段提交,也叫两阶段提交(two-phase commit)。DTP模型落地实现的一种思想。
两阶段提交中,将事务分成两个阶段来执行:
- 阶段一
准备阶段,各个本地事务执行自己事务内的逻辑,完成提交前的准备工作。 - 阶段二
执行阶段,各个事务根据上一阶段的执行结果,进行提交或者回滚。
两阶段提交中有两个角色,协调者(coordinator)、参与者(voter)。
正常情况:
异常情况:
准备阶段协调者会询问每个参与者是否可以执行事务,每个事务参与者执行事务写入redo、undo日志,然后反馈事务的执行结果,只要有一个参与者返回的是disagree则说明失败,协调者会像各个参与者发出abort指令,各个事务收到指令后各自回滚事务。
两阶段提交的优点:
- 方案成熟,很稳
- 能保证强一致性
两阶段提交的缺点:
- 单点故障
当协调者挂了之后后续的步骤就没办法进行了,被锁住的数据没办法解锁,会造成阻塞。 - 数据锁定
分阶段提交由于过程被拆成两段,会造成本地事务的执行过程变长,数据会长时间处于锁定状态。
2.2.3.TCC模式
TCC模式是专门用来解决分阶段提交的缺陷的,减少数据锁定,避免阻塞问题。
TCC相当于是强化了两阶段提交,在分阶段提交中埋入了以下几个点位:
try
资源的检测和预留。
confirm
执行的业务操作提交,要求try成功的话confrim一定要能成功。
cancel
预留资源释放
经过TCC的强化埋点后两阶段提交变成了:
准备阶段
try,资源预留,通知每个参与者去try一下数据资源,判断一下数据资源是否足以支撑之后的事务运行。每个参与者均try成功再执行下一步,否则直接cancel。
执行阶段
confrim,让协调者通知各个参与者执行并提交事务(因为资源已经准备好了,执行和提交可以一气呵成),各个参与者confrim成功则成功,但凡有一个失败则通知协调者组织全局回滚、释放资源、退出。
TCC的优点:
TCC其实每个阶段都是单独的事务,try其实是个事务只是去摸一下看下阶段要的数据资源是否能用,confrim则是原来的包含业务逻辑的事务。这样的话就避免了两阶段提交中agree过程中对于数据的锁定,尽量避免了影响其他事务的执行。
TCC的缺点:
- 编码成本
存在代码侵入需要开发人员手动编写TCC三步,开发的成本会比较高。 - 弱一致性
- TCC保证的是最终一致性,因为单个本地事务的执行和提交都包含在confirm一个步骤中,如果出现问题回滚前其实是没有保证一致性的,在这中间有其他读操作进来是能读到已经变动的数据的。
2.2.4.可靠消息服务
可靠消息服务起源自eBay,是将各个服务的本地事务串成一个链,依托MQ来保障分布式事务,比如服务A执行完服务后向MQ中存放自己执行成功的信息,MQ再向服务B推送消息叫它执行本地事务,服务B执行完本地事务后又告诉MQ,MQ继续向后续要执行本地事务的服务推送消息。
缺点:
由于是MQ主动向后续服务推送消息,后续服务要是失败了,前置服务感知不到,前置服务无法进行感知回滚。。所以可靠消息服务适用的场景有限。
2.2.5.AT模式
AT模式是Alibaba的seata组件开源出来的一种分布式事务解决方案,是对TCC的一种优化,解决了TCC模式中的的代码侵入、编码复杂等问题。
AT模式中,用户只需关注自己的业务SQL,seata框架会自动生成书屋的二阶段提交和回滚操作。
AT模式整个流程和TCC大致相同,不同点是在于AT模式将执行放在了一阶段:
一阶段(开发者实现)
正常编写业务逻辑,然后执行SQL,执行本地事务,并返回执行结果。
二阶段(框架托管)
根据一阶段的结果判断二阶段的做法,提交或者回滚。
AT模式的底层原理:
在阶段一的时候拦截下所有SQL,框架会去解析这条SQL然后去数据库中查询出要操作的数据,存一份镜像叫before image,接下来执行SQL,执行完SQL后再查询一次,存一份叫after image。
到了阶段二的时候,比对before镜像和after镜像从而判断操作是否成功,如果一阶段的所有本地事务操作都是成功的,就会清空before镜像和after镜像,如果有一个是失败的各个本地事务就会根据属于自己的before镜像进行回滚。
after镜像是为了在回滚的时候进行比对,要是回滚时发现数据库中此时的数据和after镜像中记录的数据不相同,则说明数据又被动过了,产生了脏数据,此时就需要人工介入了。由于行锁的存在,产生脏数据的概率很低很低,after镜像只是留了一手。
AT模式中的角色:
AT模式中一共有三个角色
TC
服务协调者,是一个单独的服务。
TM
通信中间件
RM
资源管理器,管理分支的事务处理,与TC进行通信注册分支事务和报告事务状态,驱动分支事务提交或者回滚。
TM、RM和服务是耦合在一起的,但是不侵入代码,以jar包的方式体现。
2.2.6.Seata
Seata是目前为止常用、流行切稳定的分布式事务解决方案,其在使用上对代码没有侵入,直接是基于配置的,使用方法见官方手册即可。在遇到分布式事务场景时,可以优先考虑使用seata来解决。