脏读、不可重复读、幻读和数据库隔离级别
maiaimei 2021/3/2
# 脏读
脏读,读取到未提交的数据。脏读一般针对的是update操作。
时间点 | 事务T1 | 事务T2 |
---|---|---|
1 | 开启事务 | |
2 | 开启事务 | |
3 | 第一次查询,数据为100条 | |
4 | 插入一条数据 | |
5 | 第二次查询,数据为101条 | |
6 | 回滚事务 |
时间点 | 事务T1 | 事务T2 |
---|---|---|
1 | 开启事务 | |
2 | 开启事务 | |
3 | 第一次查询,某字段值为X | |
4 | 更新某字段值为Y | |
5 | 第二次查询,某字段值为Y | |
6 | 回滚事务 |
# 不可重复读
不可重复读,一个事务范围内多个相同的查询返回了不同的结果。
时间点 | 事务T1 | 事务T2 |
---|---|---|
1 | 开启事务 | |
2 | 开启事务 | |
3 | 第一次查询,数据为100条 | |
4 | 插入一条数据 | |
5 | 提交事务 | |
6 | 第二次查询,数据为101条 |
时间点 | 事务T1 | 事务T2 |
---|---|---|
1 | 开启事务 | |
2 | 开启事务 | |
3 | 第一次查询,某字段值为X | |
4 | 更新某字段值为Y | |
5 | 提交事务 | |
6 | 第二次查询,某字段值为Y |
# 幻读
幻读是指当事务不是独立执行时发生的一种现象。
时间点 | 事务T1 | 事务T2 |
---|---|---|
1 | 开启事务 | |
2 | 开启事务 | |
3 | 查询数据“张三”,不存在 | |
4 | 插入数据“张三” | |
5 | 提交事务 | |
6 | 查询数据“张三”,不存在 | |
7 | 插入数据“张三”,不成功 |
事务T1查询“张三”,查询不到,插入又不成功,“张三”这条数据就像幻觉一样出现。
时间点 | 事务T1 | 事务T2 |
---|---|---|
1 | 开启事务 | 开启事务 |
2 | 修改全部行,某字段由“1”改为“2” | 新增行,某字段为“1” |
3 | 提交事务 | 提交事务 |
操作事务T1的用户,如果再查刚刚修改的数据,会发现还有一条数据没有修改,其实这行是由事务T2新增的,就好像产生幻觉一样。
不可重复读和幻读都是读取了已提交事务的数据,这一点与脏读不一样。
# 数据库的隔离级别
- 读未提交(read uncommitted):允许读取未提交的数据,即“读脏”。并发最高,一致性最差的隔离级别。
SELECT * FROM table_name WITH(NOLOCK)
1
读已提交(read committed):只读取已提交的数据,但不要求可重复读。可避免脏读的发生。这是许多数据库系统默认的隔离级别,除了MySQL。
可重复读(repeatable read):只允许读取已提交的数据,而且在一个事务两次读取一个数据项期间,其他事务不得更新该数据。但该事务不要求与其他事务可串行化。可避免脏读、不可重复读的发生。MySQL默认的隔离级别。
可串行化(serializable ):通常保证可串行化调度。事务T1、T2、T3排着队来执行。一致性最好,性能最差。可避免脏读、不可重复读、幻读的发生。
在MySQL数据库中,支持上面四种隔离级别,默认的为repeatable read (可重复读) ;而在 Oracle数据库 中,只支持serializable (可串行化) 级别和 read committed (读已提交) 这两种级别,其中默认的为 read committed(读已提交) 级别。