事务隔离级别
什么叫做“事务隔离”,事务隔离的意思是指多个事务同时在进行中时,各大事务被隔离开来,它们相互之间的影响和事务的隔离级别有关,按照“读未提交”–>“读已提交”–>“可重复读”–>“串行化”的顺序,越往后面隔离级别越高,事务之间的影响越小。
下面的内容都以这个数据表为例进行说明:
读未提交和脏读
在两个同时进行的事务A、B场景当中,A读到B还未提交的的数据,这种事务隔离级别会存在脏读的问题,如:
B修改id=2的num为10,但是尚未提交,然后A读取id为2的num,然后读到的值是10,然后B因为某种原因(可能出现了系统异常)导致事务回滚了,此时2最终的num仍然是100。这时的A读到了脏数据。
A B
begin
update table set num = 200 where id = 2
select num from table where id = 2
rollback
读已提交和不可重复读
要解决脏读的问题,我们可以将事务隔离级别调整到下一个阶段---“读已提交”,这种隔离级别下,事务A只能读到事务B提交后的数据,这样的话就解决了上一个隔离级别的脏读问题,但是无法解决可重复读的问题,例如:在事务A中,两次执行相同的读取语句,读到的内容却是不一样的。举例如下
A B
begin begin
update table set num = 10 where id = 2
select num from table where id = 2 (第一次)
commit
select num from table where id = 2 (第二次)
commit
事务A中第一次读的时候,读到的num是100,第二次再执行读的时候,读取到的num是10。这就是不可重复读问题。
可重复读
同样为了解决上一级别隔离事务带来的不可重复读的问题,我们可以将事务隔离级别调整到“可重复读”,这样就可以保证在同一个事务中只会读取到当前事务对数据的修改,其他事务修改的数据不会影响到当前事务的任何一次读取。还是上面的例子,事务A两次执行读取到的结果都是100,即可重复读.
幻读
可重复读虽然解决了不可重复读的问题,但是仍然存在幻读的问题。
幻读也有很多版本。很多小伙伴可能对幻读也有一些不太理解。
那么什么是真正的幻读呢?Show Time
A B
begin begin
select num from table where id > 2
insert into table values(3,30)
commit
update table set num = 0 where id > 2
select num from table where id > 2
commit
在B事务提交之后,A事务第二次select之前,先进行一次update操作,然后A再次执行select时,id=3的行就会出现,而且num的值是0.出现了幻读。
出现幻读的原因
MVCC只对读有效,对写操作无效,由于update是写操作,所以为更新B已经插入的id=3的行,将num更新成0,此时id=3的行被A事务(当前事务)修改了,所以A事务中第二次select时,是可以看到被当前事务修改(update)的数据的,所以id=3的行会出现在select的结果中,这就是幻读出现的原因。
事务调度
一组事务的基本读、写、加锁、解锁等其他控制操作的一种执行顺序称为对这组事务的一个调度。
并发调度:多个事务从宏观上看是并发执行的,但从微观(基本读写操作)上来看则是交叉执行的,前面也有提到。
并发调度的正确性:当且仅当在这个并发调度下所得到的新数据库结果与分别串行地运行这些事务所得的新数据库完全一致,则说调度是正确的。
可串行性
如果不管数据库初始状态如何,一个调度对数据库的影响都和某个串行调度相同。则我们说这个调度是可串行化的。
串行化:将所有的事务排队,一个接一个的进行,不存在多个事务同事进行的情况,也就类似于同步模式,所以串行的事务隔离级别不会出现前面提到的脏读、不可重复读、幻读等任何一种问题。但是你所需要付出的是---严重影响了数据库的性能。
事务隔离级别的选择
事务隔离级别需要根据具体的业务场景来选择,并没有哪一个级别是万能的。
有些场景下,甚至根本不需要事务,这时候,也许MyIsam引擎才是最合适的。mysql的默认事务隔离级别是可重复读,你可以根据自己的需求,把mysql的隔离级别调整到“读已提交”。Oracle的默认事务隔离级别是读已提交。
“读未提交”和“串行性”这两种隔离级别因为脏读和性能的问题,相对来说使用很少。