问题
-
事务的几个特点
-
数据库事务有哪些隔离级别
-
MySQL的默认隔离级别
考点
mysql开发的三个基本面:存储引擎(了解)、索引(重要)、事务(了解原生及spring事务)
回答
事务特点
ACID
A原子性: 事务里的所有sql, 要么都成功, 要么都不成功
C一致性(不是很懂, 抄来的): 事务开始/结束不影响数据库的一致性
-
数据库机制层面
数据符合设置的约束条件, 由唯一索引/外键/check约束/触发器保证 -
业务层面
由研发人员保证, 但一般会转为数据库机制层面保证
I隔离性: 设计到事务的隔离机制, 根据机制的选择不同, 各个事务之间的结果也不同
D持久性: 落库, 只要提交就一定成功, 即使是断电也会通过数据库的机制保证成功(redolog)
I隔离性相关的隔离级别
四个隔离级别
读未提交
一个事务还没提交, 另一个事务就读到了结果...
读已提交(不可重复读)
(Oracle默认级别)
能读取到已经提交的事务结果(针对单条数据)
举例:
事务A, 先查一次, 结果为1
事务B, 在事务A查询之后, 改成结果2
事务A, 再查一次, 结果为2
可重复读
一个事务中的查询结果是不变的(针对单条数据)
(mysql默认隔离级别, mysql里该级别可以解决幻读)
举例:
事务A, 开始查的是1, 即使中途有其他事务改了结果, 事务A查出来还是1
串行化
事务A执行完毕, 才能执行事务B
主要是为了防止幻读:
幻读针对插入, 比如事务A已经将所有的行的数据修改为2了, 但是另一个事务B插入了一条数据为1, 那么事务A此时再查询就会有未修改的一条数据
mysql默认隔离级别是如何解决幻读
通过MVCC机制(多版本并发控制,multi-version concurrency control), 类似于快照。
简单来说就是多两个隐藏列: 该行创建的事务id, 该行删除时的事务id
查询默认带条件: 创建事务id <= 当前事务id && 当前事务id < 删除事务id
举例:
id | name | 创建事务id | 删除事务id |
---|---|---|---|
1 | 张三 | 120 | 空 |
2 | 李四 | 119 | 空 |
事务A, 事务id: 121
事务B, 事务id: 122, 把id2的李四改成了小李四, 此时插入一条数据; 再把id=1的数据删除
id | name | 创建事务id | 删除事务id |
---|---|---|---|
1 | 张三 | 120 | 122 |
2 | 李四 | 119 | 空 |
2 | 小李四 | 122 | 空 |
事务A, 事务id: 121
查询id=1, 可以查到(当前事务id 121 < 删除事务id 122);
查询id=2, 还是李四(创建事务id 120 <= 当前事务id 121 && 当前事务id 121< 删除事务id 122)
事务B, 事务id: 122
查询id=1, 查不到(当前事务id 122 不小于 删除事务id 122);
查询id=2, 查询为小李四(创建事务id 122 <= 当前事务id 122)
spring的事务机制
记得把日志的级别开成 debug
测试三个状态
伪代码
都成功:
serviceA save () {
insert();
serviceB.delete();
}
调用方在被调用方执行完毕后报错:
serviceA save () {
insert();
serviceB.delete();
throws Exception;
}
被调方报错;
serviceA save () {
insert();
serviceB.delete( throw Exception);
}
REQUIRED, 被调用方(ServiceB)开启事务, 如果有调用方ServiceA有事务则加入调用方的事务, 两者合成一个事务, 任何一方出错, 全部回滚; 如果调用方ServiceA没有事务, 则被调用方ServiceB创建单独的事务, ServiceB出错的回滚自己, 和ServiceA无关, 即只有B的事务
- ServiceA, REQUIRED
- ServiceB, REQUIRED
合并到一个A的事务中
NOT_SUPPORTED, Spring不为当前方法开启事务,相当于没有事务, 调用方A有事务, 会无法执行B为NOT_SUPPORTED, 会报错
- ServiceA, NOT_SUPPORTED
- ServiceB, REQUIRED
B会开事务, A不会, 所以只会回滚B
REQUIRES_NEW, 不管是否存在事务,都创建一个新的事务,原来的方法挂起,新的方法执行完毕后,继续执行老的事务
- A: REQUIRED,
- B: REQUIRES_NEW,
B事务报错, A事务catch了, 那么只会B事务回滚, A事务照常
MANDATORY, 必须要在一个事务中, 不然就报错
- A: 无事务,
- B: MANDATORY,
报错:
No existing transaction found for transaction marked with propagation 'mandatory'
NEVER, 必须在无事务环境下执行, 不然就报错
- A: REQUIRED,
- B: NEVER,
报错:
Existing transaction found for transaction marked with propagation 'never'
SUPPORTS, 看调用方是否存在事务, 有加入事务, 没有就不加
- A: REQUIRED,
- B: SUPPORTS,
会加入A事务, 如果A没有事务, 则都没有事务
NESTED, 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作
- A: REQUIRED,
- B: NESTED,
还要看mysql的环境,
The MySQL server is running with the --transaction-write-set-extraction!=OFF option so it cannot execute this statement
会报错, NESTED会不成功
据说执行的效果,save方法创建一个事务,则再调用delete方法时,直接在该事务的基础上创建一个嵌套事务,本质上还是同一个事务,做一次提交;
save方法不创建事务,则调用delete方法时,直接创建一个新的事务,单独提交