mysql有4种事务隔离级别,分别为:read uncommited(读未提交数据)
read commited(读已提交数据) ,repeatable read(可重复读) 和 serializable(串行化)。其默认隔离级别为为repeatable read。这些隔离级别是为了解决同时运行多个事务时,当这些事务访问数据库中相同的数据时引发的并发问题。一般有三种:脏读,不可重复读,幻读,在下面对应的隔离级别中会 解释这些问题。并且随着隔离级别的升级,会慢慢解决这些并发问题。
设置隔离级别:set session transaction isolation level 隔离级别
查看当前隔离级别:select @@tx_isolation
在详细叙述每种隔离级别之前, 先做对四种隔离级别一个总结:
- READ UNCOMMITTED:一个事务在提交之前,对其他事务是可见的,即事务可以读取未提交的数据。存在“脏读”(读到了脏数据)问题;
- READ COMMITTED:事务在提交之前,对其它事务是不可见的。存在“不可重复读”(两次查询的得到的结果可能不同,即可能在查询的间隙,有事务提交了修改)问题。解决了“脏读”问题。
- REPEATABLE READ:在同一事务中多次读取的数据是一致的。解决了脏读和不可重复读问题,存在“幻读”(在事务两次查询间隙,有其他事务又插入或删除了新的记录)。— MySQL默认隔离级别。
- SERIALIZABLE:强制事务串行化执行。即一个事物一个事物挨个来执行,可以解决上述所有问题。
①隔离级别为read uncommitted,啥都解决不了
如图,我设置当前隔离级别为read uncommited,这是最低级别的隔离。意思是在事务中做了update,insert或者delete操作,事务未提交时,在另外一个窗口查看该表,可以得到实时数据更新。
接下来我往表中插入数据,字段为id和money,插入3条数据
接下来我开启事务,并将 A的money从100更新为150,C的money从300更新到250 (id,money为字段名)。但是,我此时并未rollback或者commit,也就是我处于未提交状态。
那么,我新开了一个窗口,同样操作,开启一个新的事务,并设置相同的隔离级别,注意:这个很重要,如果没有设置相同的隔离级别,就无法实现。
可以看到,我在另一个窗口查看未提交的事务中所做的改变,可以得到相同的结果,也就是说:read uncommited隔离级别就如字面意思,可读取未提交的数据。顺带提一下,这种情况也称为 脏读,意思是对于两个事务t1,t2,当t1去读取已经被t2更新但是却未提交的数据时,可以读取到,若此时t2回滚,t1读取到的就是无效的。比如下图,我执行回滚,然后再去另一个窗口查看:
另一个窗口的事务:
很明显,再次读取时,money并未改变,因为我已经rollback了,所以这些update操作是无效的。 注意:这种隔离级别是最低的,会出现很多并发问题。
②隔离级别为read committed,解决脏读
意思是读已提交的数据,同样从字面意思理解:就是当事务发生update,insert或者delete操作,但还未提交的时候,另一个事务去读取这个表的数据,是无法读取实时更新的数据的。
如下图,还是干一样的事情,把A的money更新成150,C的money更新成250,然后我先不提交,在另一个窗口来读取这个表,看看能不能得到新的A的money和新的C的money
另一个窗口如下,很明显,我还没提交事务,且隔离级别为read committed,所以读不到更新后的,也就是解决了“脏读”问题。
接下来我commit一下,看看结果是不是可以读到更新,很明显可以了。
另一个窗口的事务:
总结一下:上面这种情况叫做不可重复读,其定义为:对于t1,t2两个事务,t1去读取已经被t2更新了但是还没提交的数据,得到一个值,然后t2提交后,t1再去读同一个数据,又得到一个值,这两个值是不同的。
那我们要解决“不可重复读”的问题,就需要继续升级隔离级别,看下面。
③ 隔离级别为repeatable read,解决脏读,不可重复读
意思是可重复读,如下图,我把A的money更新成1000,并在另一个窗口的事务来查看。
可以发现我还没提交之前,没有出现“脏读”情况,money并未改变。
接下来我在第一个事务那里commit,然后在另一个窗口中的事务继续查看,发现还是没改变,这就解决了“不可重复读”问题。
接下来我把另一个窗口的事务也提交了,再次查询可以发现money已经改变了,这就是 隔离级别为repeatable read(可重复读),可以解决脏读、不可重复读问题。
④隔离级别为serializable(串行化)。啥都能解决,但是效率低
幻读:事务t1对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,事务t2也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,操作事务t1的用户就会发现表中还存在没有修改的数据行,就好象发生了幻觉一样(嗯?我不是已经修改了全部数据行了吗?怎么还存在一个新数据行修改不到呢?)
比如下图,我开启一个事务,并打算将所有money设置为300,我的表只有3行数据。
然后同样的,我在另一个窗口开启一个新事务,然后新增一行数据。
需要注意:如果隔离级别是serializable,当在事务1中进行update操作,并且还未提交的时候,在另一个事务2中做insert操作,会阻塞在insert语句(由于我没有为任何字段创建索引,而innodb存储引擎在索引失效时会由行锁(默认)变为表锁,所以另一个事务会被阻塞)。如果隔离级别不是serializable而出现“幻读”,那么这个时候左图的update执行完后,显示的应该是Change:4。
而现在我设置隔离级别是serializable,就解决了“幻读“的问题。
总结一下:对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,会导致各种并发问题,比如上面提到的 脏读、不可重复读、幻读等等。举个例子,说两个事务并发执行,访问同一块数据,当事务1想要update,事务2想要insert,好的情况就是事务1干完update就提交,只有事务1提交完了,事务2才可以insert。这样避免上面提到的三种并发问题,而只有最高级别的serializable才可以做到,而且因为存在阻塞,所以效率并不高。也因此mysql的默认隔离级别是第三种repeatable read。
,