并发事务处理能大大增加数据库资源的利用率,提高数据库系统的事务吞吐量,从而可以支持更多用户。
但是同时会带来诸多问题
1、更新丢失(Lost Update)
两个或者多个事务同时选择同一行数据,都基于最初选定的值更新该行,由于每个事务都不知道其它事务的存在,就会发生更新丢失的问题。最后提交的更新覆盖了之前其它事务所做的更新。
2、脏读(Dirty Reads)
一个事务正在对一条记录进行修改,这个事务完成并提交前,这条记录的数据就处于不一致的状态。此时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,
并依据此做了进一步的处理,就会产生对未提交的数据的依赖关系。这种现象就叫做“脏读”。
总结:事务A读取到了事务B已经修改但尚未提交的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取的数据无效,不符合一致性要求。
3、不可重复读(Non-Repeatable Reads)
一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”,重复读到的是不同的数据。
总结:事务A读取到了事务B已经提交的修改数据,不符合隔离性。
4、幻读(Phantom Reads)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。
举个例子:比如事务A第一次查询到表Student中有a、b、c三条数据,然后事务B向里面添加一条d数据,事务A再按照原来的查询条件查询发现查询出四条数据,这让事务A产生幻想,之前
明明就只有三条数据,为什么现在却有四条数据了呢?
总结:事务A读取到了事务B提交的新增数据,不符合隔离性。
解决方案:
解决更新丢失主要有以下两个方式:
- 使用事务+锁定读,也就是for update
- 不使用事务,用CAS自旋来操作
脏读、不可重复读和幻读其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。那有哪些隔离级别呢?主要有以下四种隔离级别:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(Read uncommitted) | 不能解决 | 不能解决 | 不能解决 |
读已提交(Read committed) | 可以解决 | 不能解决 | 不能解决 |
可重复读(Repeatable read) | 可以解决 | 可以解决 | 不能解决 |
可串行化(Serializable) | 可以解决 | 可以解决 | 可以解决 |
假设现在有两个事务,事务A和事务B,那么上面的四种隔离级别是什么意思呢?
1、读未提交(Read uncommitted)
字面意思,就是可以读到别的事务未提交的数据,也就是事务A可以读取事务B未提交的数据,这种情况肯定可能会导致脏读、不可重复度和幻读。
2、读已提交(Read committed)
字面意思,就是只可以读到别的事务已提交的数据,也就是事务A只可以读取到事务B已经提交了的数据,那么在B未提交之前的数据是读取不到的,也就不可能产生脏读,但是因为事务B已提交的
数据是可以读取到的,所以可能会导致不可重复读和幻读。
3、可重复读(Repeatable read)
字面意思,就是事务可以重复读取数据,在事务期间,每次读取的数据都是一样的,也就是事务A开启事务后,读取了某一个表的5条数据,不管你事务B怎么对这5条数据修改操作,我事务A每次
查询都是5条一摸一样的数据,所以是可重复读的,因此不可能导致脏读,不可重复读,但是还是可能导致幻读的。
4、可串行化(Serializable)
mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。
扩展:
我们都知道MySQL默认的隔离级别是可重复读(Repeatable read),那为什么不会产生幻读呢?
原因:MySQL在可重复读(Repeatable read)隔离级别的情况下使用了next-key Lock锁算法,因此可以避免幻读的产生。next-key Lock锁,锁定的不是单个值,而是一个范围(GAP),
并且锁定记录本身。对于行的查询,都是采用该方法。