Mysql 事务
事务的ACID
- A:atomic 原子性
- C:consistency 一致性
- I:Isolation 隔离性
- D:Durability 持久性
事务隔离级别
读未提交
image读已提交
同一个事务中读到读两次结果不一样,所以也叫不可重复读
image
可重复读
事务A永远读到的是事务刚开始时候的数据
image
串行化
image串行化会对表或者表中的目标行加锁,具体实现根据存储引擎来定
Innodb中的事务隔离级别和锁的关系
补充:幻读
这不是隔离级别,只是一个会出现的问题,串行化可以解决幻读
image
Mysql 怎么保证可重复读
回顾一下刚刚的图,明明事务B已经修改了name,那么事务A读的时候李四这个数据是存储在哪边的?
这就引发出MVCC机制,就是多版本并发控制 multi-version concurrency control
innodb存储引擎,会在每行数据的最后加两个隐藏列,一个保存行的创建时间,一个保存行的删除时间,但是这儿存放的不是时间,而是事务id,事务id是mysql自己维护的自增的,全局唯一。
把上图的过程用来举个例子:
事务A的事务id为1
事务B的事务id为2
step1
查询id=1,得到name=李四
id | name | 创建时间 | 删除时间创建事务id <= 当前事务id,当前事务id < 删除事务id |
---|---|---|---|
1 | 李四 | 1 | null |
step2
将id=1的name改成张三,事务还未提交
id | name | 创建时间 | 删除时间 |
---|---|---|---|
1 | 李四 | 1 | null |
1 | 张三 | 2 | null |
step3
事务id=1的事务,查询id=1的这一行的时候,一定会找到创建事务id <= 当前事务id的那一行,select * from table where id=1,就可以查到李四的那一行
补充 step4
假如事务C(事务id=3)这时候删除了李四,那么删除时间就是3
id | name | 创建时间 | 删除时间 |
---|---|---|---|
1 | 李四 | 1 | 3 |
1 | 张三 | 2 | null |
事务A查询id=1还是能查到name=4,因为查询的是
- 创建事务id <= 当前事务id
- 删除事务id > 当前事务id
事务传播级别
场景:方法A使用了 @Transactional 注解,方法B也使用了 @Transactional 注解,方法A调用了方法B,这时候就需要考虑事务传播级别。
PROPAGATION_REQUIRED
默认传播级别
B不会单独开事务,直接使用A的事务
PROPAGATION_SUPPORTS
如果B使用了该传播级别,假如A加了 @Transactional 注解,那么B使用A的事务,如果A没有加 @Transactional 注解,那么B自己也不开事务
PROPAGATION_MANDATORY
必须被开启了事务的方法调用,不然就报错
PROPAGATION_REQUIRES_NEW
强制自己开一个新的事务,假如事务B使用该隔离级别,如果A调用B的地方try catch了,那么A方法就不会回滚,B会回滚
PROPAGATION_NOT_SUPPORTED
使用了该隔离级别的方法不会加入到事务中,报错也不会回滚
PROPAGATION_NEVER
不能会开启了事务的方法调用
PROPAGATION_NESTED
开启子事务,B会回滚到开启子事务的save point