什么是事务?
定义
数据库事务是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
组成
一个数据库事务通常包含对数据库进行读或者写的一个操作序列。
目的
- 为数据库操作提供了一个从失败中恢复正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
- 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
总结
- 失败恢复方法
- 保持一致性的方法
- 操作隔离的方法
成功的情况下
能将数据从一种状态变为另外一种状态,并能够持久化。
异常情况下
- 能将数据恢复到正常状态
- 要能保证一致性,包含数据的一致性和约束的一致性
并发的情况下
并发的操作之间不能产生相互影响。
事务的特性
- 原子性(Actomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
- 一致性(Consistency):事务应确保数据库的状态从一个一致状态变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
- 隔离性(lsolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
- 持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。
事务的并发异常
- 回滚丢失/第一类更新丢失Update Lost:此种更新丢失是因为回滚的原因,所以也叫回滚丢失。
- 覆盖丢失/第二类更新丢失Seconde Update Lost:此种更新丢失是因为更新被其他事务给覆盖了,也可以叫覆盖丢失。
![](https://img.haomeiwen.com/i25428988/c1c3e7ea32ad10e6.png)
- 脏读Dirty Read:此种异常是因为一个事务读取了另一个事务修改了但是没有提交的数据。
![](https://img.haomeiwen.com/i25428988/06195cdc029fac57.png)
- 不可重复读Not Repeatable Read:这种异常是一个事务对同一行数据的执行了两次或更多次查询,但是缺得到了不同的结果。
- 幻读Phantom Read:幻读和不可重复读有点像,只是针对的不是数据的值而是数据的数量。
![](https://img.haomeiwen.com/i25428988/657b3ffd418c16f5.png)
事务隔离级别
- 读未提交Read Uncommitted:该隔离级别指即使一个事务的更新语句没有提交,但是别的事务可以读到这个改变。
- 可重复读Repeatable Read:该隔离级别指一个事务中进行两次或者多次同样的对于数据内容的查询,得到的结果是一样的,但不保证对于数据条数的查询是一样的,只要存在读改行数据就禁止写,消除了不可重复读和第二类更新丢失,这是MySQL数据库默认的隔离级别。
- 读已提交Read Committed:该隔离级别指一个事务只能看到其他事务的已经提交的更新,看不到未提交的更新,消除了脏读和第一类丢失更新,这是大多数数据库的默认隔离级别,比如Oracle、Sqlserver。
- 串行化Serializable:事务执行的时候不允许别的事务并发执行,完全串行化的读,只要存在读就禁止写,但可以同时读,消除了幻读。这是事务隔离级别的最高级别,虽然最安全最省心,但是效率太低,一般不会用。
隔离级别命令
- 查看数据库版本:select version();
- 查看隔离级别:select @ @session.tx_isolation;
- 修改隔离级别:set @session.tx_isolation=参数
- 可选参数:
- READ-UNCOMMITTED
- READ-COMMITTED
- REPEATABLE-READ
- SERIALIZABLE
- 可选参数:
数据库锁
- 悲观锁:对于数据库外界修改保持保守态度,认为数据随时会修改,所以整个数据处理中需要将数据加锁。悲观锁一般都是依靠关系数据库提供的锁机制,事实上关系数据库中的行锁,表锁不论是读写锁都是悲观锁。
- 乐观锁:每次自己操作数据的时候认为没有人会来修改它,所以不去加锁,但是在更新的时候会去判断在此期间数据有没有被修改,需要用户自己去实现。
按照性质分类:
- 共享锁(Share locks简称为S锁):也称读锁,事务A对对象T加S锁,其他事务也只能对T加S,多个事务可以同时读,但不能有写操作,直到A释放S锁。
- 排它锁:(Exclusive locks简称为X锁):称为写锁,事务A对对象T加X锁以后,其他事务不能对T加任何锁,只有事务A可以读写对象直到A释放X锁。
- 更新锁:(Upadate locks简称为U锁):用来预定要对此对象施加X锁,它允许其他事务读,但是不允许再施加U锁或X锁;当被读取的对象将要被更新时,则升级为X锁,主要是用来防止死锁的。
MySQL行锁-同一条数据加锁
Innodb的特性:
- 支持行级锁、表锁,支持事务,支持外键,支持非锁定读(默认读取操作不会产生锁)。
- 通过多版本并发控制(MVCC)获得高并发性,并实现SQL标准的4种隔离级别。
- 使用Next-key Locking策略避免幻读现象的产生。
- 供插入缓冲,二次写,自适应哈希索引,预读高性能和高可用的功能。
![](https://img.haomeiwen.com/i25428988/bbffd02c524303b9.png)
/*加S锁语句*/
select * from T where id = 1 lock in share mode;
/*加X锁语句*/
select * from T where id =1 for update;
![](https://img.haomeiwen.com/i25428988/f8c7068a5fdbb7b4.png)
![](https://img.haomeiwen.com/i25428988/27bbe488e2ea77be.png)
![](https://img.haomeiwen.com/i25428988/eb2edf701f577d00.png)
MySQL行级锁-不同行数据加锁
![](https://img.haomeiwen.com/i25428988/f4e16230a39e62c6.png)
![](https://img.haomeiwen.com/i25428988/e452d715cd5bc3fc.png)
![](https://img.haomeiwen.com/i25428988/66fd38a28838d9ca.png)
MySQL行级锁-全记录加锁
![](https://img.haomeiwen.com/i25428988/420414cf8c2adbd9.png)
![](https://img.haomeiwen.com/i25428988/23c31a8491a9930a.png)
- 全记录S+行S=共享
- 表X+行X=互斥
索引与锁的关系
![](https://img.haomeiwen.com/i25428988/ae2520a3d847405f.png)
MySQL的Innodb是按照索引进行加锁的如果语句没有使用索引,则引起全表扫描,导致全表数据被锁,所以其他事务再想锁定一行没办法锁定了。
间隙锁、Next-key锁
间隙锁
![](https://img.haomeiwen.com/i25428988/9429a4d8450d2707.png)
间隙锁英文Gap Lock
过程:
(负无穷,11)(11,22)(22,33)(33,44)(44,正无穷)
因此ono=22的数据被加X锁,则(11,22)(22,33)也会被加锁,有效防止了幻读的产生,隔离级别必须为可重复读。
![](https://img.haomeiwen.com/i25428988/10353cdfa10d5142.png)
Next-Key锁
过程:
(负无穷,11)(11,22)(22,33)(33,44)(44,正无穷)
因此ono=22的数据被加X锁,先会给其对应的主键索引加上行锁。然后在(11,22)(22,33)加上间隙锁,有效的放置了幻读的产生,隔离界别必须为可重复读。
死锁
![](https://img.haomeiwen.com/i25428988/b515e0ca64be6b8b.png)
查看死锁日志
show engine innodb status;
show engine innodb status\G;
意向锁(表级锁)
表级锁/意向锁
锁简称 | 全称 | 说明 |
---|---|---|
IS锁 | 意向共享锁 | 告诉事务稍后要向表中的个别记录增加S锁,当对某条数据加S锁的时候,必须先向表上加IS锁。 |
IX锁 | 意向排它锁 | 告诉事务稍后想表中的个别记录增加X锁,当对某条数据加X锁的时候,必须先向表上加IX锁。 |
S锁 | 共享锁 | LOCK table...READ增加表级S锁 |
X锁 | 排它锁 | LOCK TABLE...WRITE增加表级X锁 |
兼容关系
锁 | X | IX | S | IS |
---|---|---|---|---|
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 兼容 | 冲突 | 兼容 |
S | 冲突 | 冲突 | 兼容 | 兼容 |
IS | 冲突 | 兼容 | 兼容 | 兼容 |
表级锁只会与表级锁冲突,行级锁只会和行级锁冲突。
意向锁的作用
例如向一个表添加表级X锁的时候
- 如果没有意向锁的话,则需要遍历所有整个表判断是否有行锁的存在,以免发生冲突。
- 如果有了意向锁,只需要判断该意向锁与即将添加的表级锁是否兼容即可。因为意向锁的存在代表了有行级锁的存在或者即将有行级锁的存在。因而无需遍历整个表,即可获取结果。
表级锁操作
![](https://img.haomeiwen.com/i25428988/5d9089f2789c17d8.png)
![](https://img.haomeiwen.com/i25428988/1424c8f3cd187998.png)
![](https://img.haomeiwen.com/i25428988/82e0263bcdcb21e2.png)
![](https://img.haomeiwen.com/i25428988/b39494cc3a985f5d.png)
- 表锁:不会出现死锁,发生锁冲突几率高,并发低
- 行锁:会出现死锁,发生锁冲突几率低,并发高
意向补充锁
补充:
- 在表级上加S、X锁,上面的查询事务的三条语句是查询不到事务的,即使是在事务里做锁表。
- IS、IX锁只能去看详细的MySQL日志才能看到。
乐观锁的实现侧策
- version字段
- 时间戳
- 待更新字段 Wait Update Column(类似于把字段拼接到sql里进行匹配,跟version差不多)
- 所有字段 All Column(所有字段做对比)
MVCC多版本并发控制深度解析一
思考
-
MySQL是如何保证高并发的?
隔离级别、锁机制
-
多个事务并发读取一条数据需要加锁吗?
不需要
-
一个事务读数据,另外一个事务写数据需要加锁吗?
当前读需要加锁、快照读不需要加锁
-
一个事务在写数据,另外一个事务也在写数据需要加锁吗?
加锁
基础
- MVCC全称是Multi-Version Concurrency Control,即多版本并发控制。
- 主要是为了提高数据库的并发性能。
- 只有InnoDB引擎支持MVCC。
- 只有InnoDB引擎支持事务。
查询方式
- 当前读:它读取的数据库记录,都是当前最新的版本,会对当前读取的数据进行加锁,防止其他事务修改数据。是悲观锁的一种操作。
- select lock in share mode
- select for update
- update
- insert
- delete
- 串行化级别的select
- 快照读:不加锁的select操作,隔离级别不能是串行化。
MVCC多版本并发控制深度解析二
版本链
数据行的三个隐藏字段
字段 | 描述 |
---|---|
db_trx_id | 6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID |
db_row_id | 6byte,隐含的自增ID(隐藏主键),如果数据库表没有主键,InnoDB会自动以db_row_id产生一个聚簇索引 |
db_roll_pointer(版本链关键) | 7byte,回滚指针,指向这条记录的上个版本(存储与rollback segment里) |
undolog
- 每次对数据库记录进行改动,都会记录一条undo日志。
- 每条undo日志也都有一个roll_pointer属性,可以将这些undo日志都连起来,串成一个链表INSERT操作对应的undo日志没有该属性,因为该记录并没有更早的版本。
- 当时事务进行回滚时可以通过undolog里的日志进行数据还原。原子性也是依靠undolog完成的。
Read View
-
作用
- 事务进行快照读的时候生产读视图Read View
- 在该事务执行的快照读的那一刻,会生成数据库当前的一个快照
- Read View主要是用来做可见性判断的,把它比作条件用来判断当前事务能够看到哪个版本的数据,即可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据
-
结构
- trx_ids:当前系统活跃(未提交)事务版本号集合
- low_limit_id:创建当前read view时“当前系统最大事务版本号+1”
- up_limit_id:创建当前read view时“系统正处于活跃事务最小版本号”
- create_trx_id:创建当前read view的事务版本号
![](https://img.haomeiwen.com/i25428988/733522c56fdeb678.png)
-
可见算法
- db_trx_id < up_limit_id || db_trx_id = = creator_trx_id(显示)。如果数据事务ID小于read view中的最小活跃事务ID,则可以肯定数据是在当前事务开启之前就已经存在了,所以可以显示。或者数据的事务ID等于create_trx_id,那么说明这个数据就是当前事务自己生成的,自己生成的数据自己当然能看见,所以这种情况下次数据也是可以显示的。
- db_trx_id > = low_limit_id(不显示)。如果数据事务ID大于read view中的当前系统最大的事务ID,则说明该数据是在当前read view创建之后才产生的,所以数据不显示,如果小于则进入下一个判读。
- db_trx_id是否活跃事务(trx_ids)中
- 不存在:则说明read view产生的时候事务已经commit了,这种情况数据可以显示。
- 存在:则代表read view生成时刻,你这个事务还在活跃,还没有commit,你修改的数据,我当前事务也是看不见的。
-
创建时机
- RC读已提交隔离级别:是每个快照都会生成并获取最新的Read View
- RR可重复读隔离级别:则是同一个事务中的第一个快照才会创建Read View,之后的快照获取的都是同一个Read View,之后的查询就不会重新创建,所以一个事务的查询结果每次都是一样的。
幻读问题和解决方案
幻读的现象
在同一个事务中,不同的位置,读取的数据条数不一致。
例子:读取数据有3条,更新数据缺更新了4条
产生的原因
同一个事务的不同的位置,读取方式发生了变化。
如果我们在一个事务当中,既有快照读也有当前读,那么就可能产生幻读问题。
解决方案
如果我们在一个事务当中完全使用快照读或当前读,那么就不会有幻读问题。
一个业务真的能满足上述条件吗?
对于可能会产生幻读的业务,我们应该在更新数据之前,查询数据的时候使用当前读(可以使用加锁进行当前读)。
例子
事务1 | 事务2 |
---|---|
begin | begin |
select * from user | select * from user |
insert into... | - |
commit | - |
select * from user for update | |
update user set... |
一般使用的就是RR级别,默认隔离级别。
是否所有的事务都要使用当前读?
不确定,需要根据业务来。在RR级别下解决幻读问题,由开发人员决定,而不是由MySQL来决定的。
S串行化全部都是当前读,不会有幻读问题。
Spring事务注解
参数名称 | 功能描述 |
---|---|
readOnly | 该属性用于设置当前事务是否是只读事务,设置true表示只读,false则表示可读写,默认false |
rollbackFor | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚 |
rollbackForClassName | 该属性用于设置需要进行回滚的异常名称数组,当方法中抛出指定异常名称数组中的异常时,则进行回滚 |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚 |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常名称数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚 |
propagation | 该属性用于设置事务的传播行为。例如Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
issolation | 该属性用于设置底层数据库事务的隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
timeout | 该属性用于设置事务的超时描述,默认为-1表示永不超时 |
Spring事务的传播行为
传播行为 | 含义 |
---|---|
REQUIRED(默认) | 表示当前方法必须在一个具有事务的上下文中运行,如果有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚) |
SUPPORTS | 表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行。 |
MANDATORY | 表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常。 |
NESTED(需要在不同的Service调用) | 表示如果当前方法有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立与被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同PROPAGATION_REQUIRED的一样。注意:这个设置只针对JDBC DataSourceTransactionManager有效,并且必须基于JDBC3.0。 |
NEVER | 表示当方法事务不应该在一个事务中运行,如果存在一个事务,则抛出异常。 |
REQUIRES_NEW(需要在不同的Service调用) | 表示当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期间被挂起,知道新的事务提交或者回滚才恢复执行。 |
NOT_SUPPORTED | 表示该方法不应该在一个事务中运行。如果有一个事务正在运行,它将在运行期被挂起,直到这个事务提交或回滚才恢复执行 |
Spring隔离级别
隔离级别 | 含义 |
---|---|
idefault | 使用数据库默认的事务隔离级别 |
read_uncommitted | 允许读取尚未提交的修改,可能导致脏读、幻读和不可重复读 |
read_committed | 允许从已经提交的事务读取,可防止脏读、但幻读,不可重复读仍然有可能发生 |
repeatable_read | 对相同字段的多次读取的结果是一致的,除非数据被当前事务自身修改。可防止脏读和不可重复读,但是幻读仍有可能发生 |
serializable | 完全服从acid的原则,确保不发生脏读、不可重复读和幻读,但执行效率最低 |
分布式事务CAP定理和BASE理论
分布式事务问题
![](https://img.haomeiwen.com/i25428988/9aaa2602a4cea12e.png)
情况二和情况三就是我们所需要面临的问题。
CAP原则
又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。CAP原则是NOSQL数据库的基石。
CAP | 说明 |
---|---|
一致性C | 在分布式系统中的所有数据备份,在同一个时刻是否同样的值。(等同于所有的节点访问同一份最新的数据副本) |
可用性A | 在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性) |
分区容错性P | 以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限能达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。 |
BASE理论
是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写。
BASE是对CAP中一致性和可用性的权衡结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的。
核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性
BASE | 说明 |
---|---|
基本可用BA | 基本可用是指分布式系统在出现不可预知的故障的时候,允许损失部分可用性。 |
软状态S | 是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。 |
最终一致性E | 最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。 |
XA协议
分布式事务解决方案:
两阶段提交型、三阶段提交型、TCC补偿型、异步确保型,最大努力通知型几种解决方案。
![](https://img.haomeiwen.com/i25428988/155cc9ceb4802151.png)
XA协议由Tuxedo首先提出的,并交给X/Open,做完资源管理器(数据库)与事务管理器的接口标准。目前Oracle、Informix、DB2和Sybase等各大数据库厂家都提供对XA的支持。XA协议才用两阶段提交方式来管理分布式事务。XA接口提供资源管理器与事务管理器之间进行通信的标准接口。
对应JAVA的实现为JTA/JTS,JTA可以说是定义了一套实现XA协议的接口规范,而JTS就是JTA的具体实现。
两阶段提交2PC策略
XA协议包含两阶段提交(2PC)和三阶段提交(3PC)两种实现。
正常流程:
![](https://img.haomeiwen.com/i25428988/9baaff22cde7399c.png)
协调者:事务管理器
参与者:资源管理器
异常流程:
![](https://img.haomeiwen.com/i25428988/0711dc1c919a5cbe.png)
2PC优缺点:
优点:
- 强一致性。
缺点:
- 同步阻塞问题。导致资源被锁、效率低。
- 单节点故障。一旦协调者发生故障则参与者阻塞,或者资源无法释放。
- 数据不一致。网络故障等导致部分参与者没有提交事务。
- 宕机问题。协调者发出消息后宕机,协调者收到消息后也宕机。
三阶段提交3PC策略
正常状态:
![](https://img.haomeiwen.com/i25428988/50b9def62a10c1e9.png)
第一阶段:
- CanCommit,询问每一个参与者,你能不能提交。
- 参与者去检查它所有的约束和关联关系是否满足操作这些数据,如果可以操作,则返回OK。
- 如果所有的参与者都ok,则进入第二阶段。
第二阶段
- 参与者接到PreCommit预提交,就会去记录日志然后锁住所需要的资源。
- 操作完成后通知协调者已完成,进入第三阶段。
第三阶段
- 协调者发布提交到每一个参与者。
- 参与者完成操作后,会给协调者已完成。
异常状态:
情况一:
![](https://img.haomeiwen.com/i25428988/8773643934222653.png)
情况二:
![](https://img.haomeiwen.com/i25428988/5c61f40d30bfe7d4.png)
优缺点:
优点:
- 减少阻塞:canCommit可以有效减少阻塞。
- 单节点故障:引入超时机制,一旦参与者接收不到协调者的信息则默认提交。
缺点:
- 数据不一致:由于超时机制默认提交可能会导致数据不一致。
TCC事务补偿
![](https://img.haomeiwen.com/i25428988/b1684327270de1fe.png)
TCC | 说明 |
---|---|
Try | 尝试执行业务。完成所有业务检查(一致性),预留必须业务资源(准隔离性) |
Confirm | 确认执行业务,不做任何业务检查,只使用Try阶段预留的业务资源 |
Cancel | 取消执行业务释放Try阶段预留的业务资源 |
主业务服务 | 主业务服务为整个业务活动的发起方 |
从业务服务 | 从业务服务负责提供TCC业务操作,是整个业务活动的操作方。从业务服务不许实现Try、Confirm和Cancel三个接口,供主业务服务调用。由于Confirm和Cancel操作可能被重复调用,故要求Confirm和Cancel两个接口必须是幂等的 |
业务活动管理器 | 业务活动管理器管理控制整个业务活动,包括记录维护TCC全局事务的事务状态和每个从业务服务的子事务状态,并在业务活动提交时确认所有的TCC型操作的confirm操作,在业务活动取消时调用所有的TCC操作的cancel操作 |
TCC实际上把数据库层的二阶段提交上提到应用层来实现,对于数据库来说是一阶段提交,规避了数据库层的2PC性能低下问题,但是TCC操作需要业务实现,开发成本较高。
异步确保型
![](https://img.haomeiwen.com/i25428988/5b660332617411ec.png)
把一个同步的流程改为异步了,中间加入了消息中间件。
优缺点
优点:
- 异步高性能:异步处理,不会大面积锁定资源。
- 准实时一致性:MQ的准实时特性。
- 服务器解耦:MQ自身特性导致。
缺点:
- 依赖于MQ事务:多数MQ不支持事务,Rocket支持。
- 消费者幂等性:多次请求与一次请求结果相同。
最大努力通知型
![](https://img.haomeiwen.com/i25428988/9e66225be00d152e.png)
主动方(支付系统,发起通知的一方),被动方(订单系统,接收通知的一方)
- 被动方的处理结果不影响主动方的处理结果
- 适用于对业务系统最终一致性的时间敏感度低的系统
- 适合跨企业的系统间的操作,或者企业内部比较独立的系统间的操作,例如消息通知
构成
- 实时消息服务(MQ或实时接口):接收方主动发送的通知消息。
- 通知服务子系统:监听MQ消息、或实时接口消息;当收到消息后,向被动方发送通知,同事生成通知记录。如果没有接收到被动方的返回消息,就根据通知记录进行重复通知。
- 提供一个幂等的结果查询接口
可以不把它看作为一种分布式事务解决方案,只认为是一种跨平台的数据处理方案也可以。
SEATA分布式事务框架
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
特色功能:
- 微服务框架支持:目前已支持Dubbo、Spring Cloud、Sofa-RPC、Motan 和 gRPC 等RPC框架,其他框架持续集成中
- AT 模式(两阶段提交):提供无侵入自动补偿的事务模式,目前已支持MySQL、Oracle、PostgreSQL、TiDB 和 MariaDB。H2、DB2、SQLServer、达梦开发中
- TCC 模式(事务补偿):支持 TCC 模式并可与 AT 混用,灵活度更高
- SAGA 模式:为长事务提供有效的解决方案,提供编排式与注解式(开发中)
- XA 模式:支持已实现 XA 接口的数据库的 XA 模式,目前已支持MySQL、Oracle、TiDB和MariaDB
- 高可用:支持计算分离集群模式,水平扩展能力强的数据库和 Redis 存储模式.Raft模式Preview阶段
seata的AT、TCC、SAGA模式原理
AT模式
前提
- 基于支持本地 ACID 事务的关系型数据库。
- Java 应用,通过 JDBC 访问数据库。
整体机制
两阶段提交协议的演变:
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
![](https://img.haomeiwen.com/i25428988/fa7761a378b16b22.png)
TC:事务控制器
TM:事务管理器
RM:资源管理器
TM开启一个全局事务,向TC发送一个begin请求。TC接收到请求后会返回一个XID(本次全局事务的ID),然后各个分支的事务开始做自己的事情了。各个微服务本地的RM都要像TC去注册它自己的分支事务,方便知道提交或者回滚哪些事务。
如果每个事务都已经完成,那么TM要向TC发送请求,通知TC全局事务要提交了。
如果中间某个事务出现问题了,这个时候TM就会告诉TC,出现错误了,我需要回滚,TC会通知各个分支事务进行回滚。
写隔离
- 一阶段本地事务提交前,需要确保先拿到 全局锁 。
- 拿不到 全局锁 ,不能提交本地事务。
- 拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
读隔离
在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted) 。
TCC 模式
回顾总览中的描述:一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:
- 一阶段 prepare 行为
- 二阶段 commit 或 rollback 行为
![](https://img.haomeiwen.com/i25428988/cd82f5b04b956c81.png)
Saga 模式
Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
![](https://img.haomeiwen.com/i25428988/7e0a1fd04fc06f1c.png)
适用场景:
- 业务流程长、业务流程多
- 参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口
优势:
- 一阶段提交本地事务,无锁,高性能
- 事件驱动架构,参与者可异步执行,高吞吐
- 补偿服务易于实现
缺点:
- 不保证隔离性(应对方案见用户文档)
网友评论