一、引言
脏读、不可重复读和幻读是数据库中由于 并发访问 导致的数据读取问题。当多个事务同时进行时可以通过修改数据库事务的隔离级别来处理这三个问题。
二、问题解释
1、脏读(读取未提交的数据)
脏读又称无效数据的读出,是指在数据库访问中,事务 A 对一个值做修改,事务 B 读取这个值,但是由于某种原因事务 A 回滚撤销了对这个值得修改,这就导致事务 B 读取到的值是无效数据。
2、不可重复读(前后数据多次读取,结果集内容不一致)
不可重复读即当事务 A 按照查询条件得到了一个结果集,这时事务 B 对事务 A 查询的结果集数据做了修改操作,之后事务 A 为了数据校验继续按照之前的查询条件得到的结果集 与前一次查询不同 ,导致不可重复读取原始数据。
3、幻读(前后数据多次读取,结果集数量不一致)
幻读是指当事务 A 按照查询条件得到了一个结果集,这时事务 B 对事务 A 查询的结果集数据做新增操作,之后事务 A 继续按照之前的查询条件得到的结果集平白无故 多了几条数据 ,好像出现了幻觉一样。
三、事务隔离
在并发条件下会出现上述问题,如何着手解决他们保证我们程序运行的正确性是非常重要的。数据库提供了 Read uncommitted 、Read committed 、Repeatable read 、Serializable 四种事务隔离级别来解决脏读、幻读和不可重复读问题,同时容易想到,可以通过加锁的方式实现事务隔离。
在数据库的增删改查操作中,insert 、delete 、update 都会加排他锁, 排它锁会阻止其他事务对其加锁的数据加任何类型的锁 。而 select 只有显示声明才会加锁。
-
Read uncommitted
读未提交,说的是一个事务可以读取到另一个事务未提交的数据修改。
读若不显式声明是不加锁的,可以直接读取到另一个事务对数据的操作,没有避免脏读、不可重复读、幻读。
-
Read committed
读已提交,说的是一个事务只能读取到另一个事务已经提交的数据修改。
很明显,这种隔离级别避免了脏读,但是可能会出现不可重复读、幻读。
-
Repeatable read
可重复读,保证了同一事务下多次读取相同的数据返回的结果集是一样的。
这种隔离级别解决了脏读和不可重复读问题,但是扔有可能出现幻读。
-
Serializable
串行化,对同一数据的读写全加锁,即对同一数据的读写全是互斥了,数据可靠行很强,但是并发性能不忍直视。
这种隔离级别虽然解决了上述三个问题,但是牺牲了性能。
总结如下表: √ 代表可能出现,× 代表不会出现。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted | √ | √ | √ |