事务隔离级别
先说什么叫做“事务隔离”,事务隔离是指多个事务同时在进行中(如果只有一个事务,那就无所谓隔离不隔离了)时,各个事务被隔离开来,相互之间的影响和事物的隔离级别有关,按照“读未提交”–>“读已提交”–>“可重复读”–>“串行化”的顺序,越往后面隔离级别越高,事务之间的影响越小。
下面的内容都以这个数据表为例进行说明:
id | money |
---|---|
1 | 10 |
2 | 20 |
4 | 40 |
读未提交和脏读
两个同时进行的事务A、B,A会读到B还未提交的的数据,这种事务隔离级别会存在脏读的问题,举个例子:
B修改id=2的money为200,但是尚未提交,然后A读取id为2的money,这是读到的值是200,然后B因为某种原因(比如系统异常)导致事务回滚了,此时2最终的money仍然是20。说明A读到了脏数据。
A B
begin
update table set money = 200 where id = 2
select money from table where id = 2
rollback
读已提交和不可重复读
为了解决脏读的问题,可以将事务隔离级别调整到“读已提交”,这种隔离级别下,事务A只能读到事务B提交后的数据,但是这种隔离级别虽然解决了脏读的问题,但是无法解决可重复读的问题,即在事务A中,两次执行相同的读取语句,读到的内容却是不一样的。举例如下
A B
begin begin
update table set money = 200 where id = 2
select money from table where id = 2
commit
select money from table where id = 2
commit
事务A中第一次select时,读到的值是20,第二次再执行select时,读取到的值是200。这就是不可重复读。
可重复读
为了解决不可重复读的问题,可以将事务隔离级别调整到“可重复读”,这种隔离级别下,可以保证在同一个事务中只会读取到当前事务对数据的修改,其他事务修改的数据不会影响到当前事务的任何一次读取。还是上面的例子,事务A两次select读取到的结果都是20,即可重复读.
幻读
可重复读虽然解决了不可重复读的问题,但是仍然存在幻读的问题。
错误的说法
对于幻读的解释,大多数博客中都是有问题的,甚至《高性能mysql》中,也没有详细解释幻读的问题
我们先来看看网上出现最多的幻读的解释,务必注意,这种解释是错误的:
A B
begin begin
select money from table where id > 2
inser into table values(3,30)
commit
select money from table where id > 2
commit
网上多数观点认为,当A进行范围查询时,B在其中插入一条数据,A再次执行该范围查询是,就会比第一次查询时多出来B刚才插入的那条数据,像幻影一样,所以出现了幻读。
为什么这种说法是不对的呢?
从实践上看,直接去msql中试试就会发现,其实A连个执行的查询结果仍然是一样的,并不会出现幻读的现象。
从原理上看,可重复读是靠MVCC(多版本并发控制)保证的,该模式下,保证事务只能读取到当前事务开启之前已经提交的事务进行的修改以及当前事务本身对数据的修改。按照这个定义,上述实验中的B事务并不会影响到A的读取。
真正的幻读
那么什么事真正的幻读呢?来看下面的例子
A B
begin begin
select money from table where id > 2
inser into table values(3,30)
commit
update table set money = 0 where id > 2
select money from table where id > 2
commit
在B事务提交之后,A事务第二次select之前,先进行一次update操作,然后A再次执行select时,id=3的行就会出现,而且money的值是0.出现了幻读。
出现幻读的原因
MVCC只对读有效,对写操作无效,由于update是写操作,所以为更新B已经插入的id=3的行,将money更新成0,此时id=3的行被A事务(当前事务)修改了,所以A事务中第二次select时,是可以看到被当前事务修改(update)的数据的,所以id=3的行会出现在select的结果中,这就是幻读出现的原因。
串行
串行将所有的事务一个接一个的进行,不存在多个事务同事进行的情况,所以串行的事务隔离级别不会出现上面提到的脏读、不可重复读、幻读等任何一种问题。代价是,严重影响了数据库的性能。
事务隔离级别的选择
事务隔离级别需要根据具体的业务场景来选择,并没有哪一个级别是万能的。
有些场景下,甚至根本不需要事务,这时候,也许MyIsam引擎才是最合适的。
Oracle的默认事务隔离级别是读已提交,而mysql的默认事务隔离级别是可重复读,根据也无需要,可以把mysql的隔离级别调整到“度已提交”。
“读未提交”和“串行”这两种隔离级别因为脏读和性能的问题,业内用的很少。