当前位置: 代码迷 >> 综合 >> MySql - 数据库的脏读、不可重复读、幻读、两类丢失更新与四大隔离级别
  详细解决方案

MySql - 数据库的脏读、不可重复读、幻读、两类丢失更新与四大隔离级别

热度:77   发布时间:2024-01-28 19:16:34.0

MySql - 数据库的脏读、不可重复读、幻读、两类丢失更新与四大隔离级别

1.什么是脏读、不可重复读、幻读、第一类丢失更新和第二类丢失更新?

2.为什么会产生脏读、不可重复读、幻读、第一类丢失更新和第二类丢失更新?

3.如何防止脏读、不可重复读、幻读、第一类丢失更新和第二类丢失更新的发生?

4.如何设置数据库隔离级别?

---------------------------------------------------------------------------------------------------------------------------

1.什么是脏读、不可重复读、幻读、第一类丢失更新和第二类丢失更新?

       脏读(dirty read):B事务读取到A事务尚未提交的更改过的数据,并在这个数据的基础上进行了操作。如果A事务回滚,那么B事务读到的数据就是脏数据,此现象称为脏读。

       不可重复读(unrepeatable read):B事务读取了A事务已经提交过的更改(或删除)的数据。如B事务第一次读取数据,然后A事务更改该数据并提交,B事务再次读取数据,导致两次读取的数据不一致,此现象称为不可重复读。

       幻读(phantom read):B事务读取了A事务已经提交的新增数据。

       第一类丢失更新: A事务撤销事务,覆盖了B事务提交的事务(现代关系型数据库中已经不会发生)。

       第二类丢失更新: A事务提交事务,覆盖了B事务提交的事务(是不可重复读的特殊情况)。

       其中不可重复读和幻读容易混淆,不可重复读侧重于数据的修改,幻读侧重于数据的新增或删除。针对不可重复读,一般采取行级锁机制,防止读到更改的数据。针对幻象读,一般采用表级锁机制,将整个表锁住,防止新增数据行。

2.为什么会产生脏读、不可重复读、幻读、第一类丢失更新和第二类丢失更新?

2.1 第一类丢失更新

       如下两个事务,事务A先开启事务查询账户有1000元,然后准备存款500元,使其账户变为1500,在此事务进行的同时,事务B进行转账操作,并提交了事务,使账户金额变为500,而事务A最后因为某些原因,没有提交事务,而是回滚了事务,将账户金额重新设置为1000。但实际上,账户已经被转走了500元,这就是第一类丢失更新产生的原因。

时间序号 事务A 事务B
T1 开启事务 开启事务
T2 查询账户余额     balance=1000 查询账户余额  balance=1000
T3 存入500             balance=1500  
T4   转出500         balance=500
T5   提交事务        balance=500
T6 取消事务,回滚 balance=1000  

2.2 脏读

       如下两个事务,事务A先开启事务查询账户有1000元,然后准备转出500元,使其账户变为500,在此事务进行的同时,事务B进行了查询操作,查询到了事务A未提交的余额500元。在事务B刚刚查询后,A事务进行了回滚,实际账户余额回复为1000元,这时事务B用错误的余额数据500元进行转入操作,并提交了事务,使账户金额变为600。但实际上,账户余额应该为1100元,这就是脏读产生的原因。

时间序号 事务A 事务B
T1 开启事务 开启事务
T2 查询账户余额     balance=1000  
T3 转出500             balance=500  
T4   查询账户余额  balance=500
T5 取消事务,回滚 balance=1000  
T6   转入100        balance=600
T7   提交事务       balance=600

2.3 不可重复读

       如下两个事务,事务A先开启事务查询账户有1000元,在此事务进行的同时,事务B也进行了查询操作,查询到账户余额为1000元。然后事务A进行了转出操作,转出了100元,余额变成了900元,并提交了事务。这时当事务B再次查询余额的时候,发现账户余额变成了900元,这就是不可重复读产生的原因。当初因为对并发事务不了解,以为事务B两次查到的数据不一致没什么影响,后来仔细想想才发现,事务B一直都没有提交,这就会产生事务B业务逻辑的失效。

时间序号 事务A 事务B
T1 开启事务 开启事务
T2 查询账户余额     balance=1000  
T3   查询账户余额  balance=1000
T4 转出100            balance=900  
T5 提交事务  
T6   查询账户余额  balance=900
T7   提交事务   

2.4 第二类丢失更新

       理解了不可重复读出现的原因后,如果把上述例子换成事务A和事务B同时进行转出操作,就是第二类丢失更新产生的原因了,因为多个事务同时对一行数据进行更新导致的问题。

时间序号 事务A 事务B
T1 开启事务 开启事务
T2 查询账户余额     balance=1000  
T3   查询账户余额  balance=1000
T4 转出100            balance=900  
T5   转出100            balance=900
T6 提交事务         balance=900  
T7   提交事务         balance=900

2.5 幻读

       A事务读取到B事务提交的新增数据,就像产生了幻觉一样,所以称作幻读,幻读一般发生在数据统计事务中。

时间序号 事务A 事务B
T1 开启事务 开启事务
T2 统计存款总金额为1000元  
T3   新增存款金额100元
T4   提交事务
T5 再次统计存款总金额为1100元  
T6 提交事务  

3.如何防止脏读、不可重复读、幻读、第一类丢失更新和第二类丢失更新的发生?

为了解决上面提及的并发问题,主流关系型数据库都会提供四种事务隔离级别。

3.1 读未提交(Read Uncommitted)

       该隔离级别所有事务都可以看到其他未提交事务的执行结果。本隔离级别是最低的隔离级别,虽然拥有超高的并发处理能力及很低的系统开销,但很少用于实际应用。因为采用这种隔离级别只能防止第一类更新丢失问题,不能解决脏读,不可重复读及幻读问题。

3.2 读已提交(Read Committed)

       这是大多数数据库系统的默认隔离级别(但不是MySQL默认)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别可以防止脏读问题,但会出现不可重复读及幻读问题。

3.3 可重复读(Repeatable Read)

       这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。这种隔离级别可以防止除幻读外的其他问题。

3.4 可串行化(Serializable)

       最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读、第二类更新丢失问题。在这个级别,可以解决上面提到的所有并发问题,但可能导致大量的超时现象和锁竞争,通常数据库不会用这个隔离级别,我们需要其他的机制来解决这些问题:比如乐观锁和悲观锁机制(下篇再详细分析)。

列个表格总结下并发一致性问题和四大隔离级别的关系

隔离级别 脏读 不可重复读 幻读 第一类丢失更新 第二类丢失更新
读未提交 不可预防 不可预防 不可预防 可以预防 不可预防
读已提交 可以预防 不可预防 不可预防 可以预防 不可预防
可重复读 可以预防 可以预防 不可预防 可以预防 可以预防
可串行化 可以预防 可以预防 可以预防 可以预防 可以预防

4.如何设置数据库隔离级别?

设置全局隔离级别:

set global transaction isolation level READ UNCOMMITTED;//读未提交
set global transaction isolation level READ COMMITTED;//读已提交
set global transaction isolation level REPEATABLE READ;//可重复读
set global transaction isolation level SERIALIZABLE;//串行化

设置session级别的隔离级别:

set session transaction isolation level READ UNCOMMITTED;//未提交读
set session transaction isolation level READ COMMITTED;//已提交度
set session transaction isolation level REPEATABLE READ;//可重复读
set session transaction isolation level SERIALIZABLE;//串行化

Spring事务:

Spring事务默认使用数据库的隔离级别,当然也可以通过@Transactional中的isolation参数调整当前Session级的隔离级别。

solation的参数有以下五种:

(1)solation.DEFAULT:为数据源的默认隔离级别

(2)isolation=Isolation.READ_UNCOMMITTED:未授权读取级别

(3)iIsolation.READ_COMMITTED:授权读取级别

(4)iIsolation.REPEATABLE_READ:可重复读取级别

(5)iIsolation.SERIALIZABLE:序列化级别

  相关解决方案