脏读、不可重复读、幻读和数据库隔离级别

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(读已提交) 级别。