背景
本地事务在分布式系统中,只能控制住自己回滚,控制不了其他服务的回滚,在分布式系统中,事务如何保证,主要是有以下两个场景:
1.远程服务假失败问题,即远程服务业务其实成功了,但是由于网络故障,服务器环境等其他问题导致,远程调用异常(导致调用方业务回滚了,远程服务没有回滚)。
2.远程服务执行完成,下面的其他方法出现异常,导致已执行的的远程请求不能回滚。(无法控制远程请求的回滚)
本地事务
一、事务的基本性质
事务的几个特性:原子性,一致性、隔离性、持久性,简称ACID
-
原子性:事务的一系列操作不可拆分,要么全部完成,要么全部失败。
-
一致性:事务在执行前后,业务整体一致。比如转账,
A:100,B:100 转:A->B:50 结果:A:50,B:150 (结果是一致的,不可能存在A扣钱了,B钱没加上的情况)
-
隔离性:事务之前相互隔离(ABCD四个事务相互隔离执行,不会存在互相影响的情况)
-
持久性:一但事务成功,数据一定会落在数据库。
二、事务的隔离级别
- 读未提交(Read uncommitted)
该隔离级别的事务,会读到其他未提交事务的数据,此现象叫脏读。
- 读已提交(Read committed)
可以读到另一个事务已提交的数据,但是事务里面多次读取可能会出现结果不一致的情况(B事务在两次读取的中间提交了事务,修改了数据,造成前后数据不一样),此现象叫不可重复读。
- 可重复读(Repeatable read)
Mysql默认隔离级别,同一个事务里,select数据库读到的数据是事务开始状态时的数据,因此在这个事务里同样的select操作结果永远一致的,解决了不可重复读问题,但是会有幻读问题。
- 串行化(Serializable )
在该隔离级别下,所有事务都是串行执行的(类似于加锁)。避免了脏读、幻读、不可重复读,但是效率极低。
三、事务传播行为
指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何运行。共7类传播行为,这里只讲最常用的:REQUIRED、REQUIRES_NEW。
REQUIRED:如果当前没有事务就创建一个事务,如果当前有事务,就加入该事务。(能上车就上车,没车上就自己来)
REQUIRES_NEW:创建一个新事务,不管当前有没有事务。(我不要你的,我要我自己的)
@Transactional //a事务
public void a() {
b(); // a事务(加入)
c(); // c事务(新事务,不回滚)
int i = 10/0; // 异常了c方法不回滚
}
@Transactional(propagation = Propagation.REQUIRED) // 有事务就加入,没有就创建
public void b() {
}
@Transactional(propagation = Propagation.REQUIRES_NEW