MySQL/InnoDB中的事务隔离级别

SQL标准中的事务四种隔离级别

隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能
  • 未提交读(Read Uncommitted):容许脏读,也就是可能读取到其余会话中未提交事务修改的数据
  • 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
  • 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,可是还存在幻象读
  • 串行读(Serializable):彻底串行化的读,每次读都须要得到表级共享锁,读写相互都会阻塞

未提交读(Read uncommitted)

数据库通常都不会用,并且任何操做都不会加锁html

mysql> select * from tx_test;
+----+-------+
| id | value |
+----+-------+
|  1 | a     |
+----+-------+
1 row in set (0.00 sec)

因为MySQL的InnoDB默认是使用的RR级别,因此咱们先要将该session开启成RU级别mysql

SET session transaction isolation level read uncommitted;


_____________________________________________________________________
事务A                                         事务B
_____________________________________________________________________
begin;              
                                                begin;
_____________________________________________________________________
                                                select * from tx_test;             
                                                ______________
                                                  id    value 
                                                ______________
                                                  1     a     
                                                ______________
___________________________________________________________________
insert into tx_test(value) values('b');
____________________________________________________________________
                                                select * from tx_test;             
                                                ______________
                                                  id    value 
                                                ______________
                                                  1     a     
                                                ______________
                                                  2     b
                                                ______________
                                                读到事务A还未commit的行,出现脏读
_____________________________________________________________________
                                                commit;
commit;
_____________________________________________________________________

已提交读(Read committed)

在RC级别中,数据的读取都是不加锁的,可是数据的写入、修改和删除是须要加锁的。sql

SET session transaction isolation level read committed;
SET SESSION binlog_format = 'ROW';(或者是MIXED)

_____________________________________________________________________
事务A                                         事务B
_____________________________________________________________________
begin;                                          begin;
_____________________________________________________________________
                                               
update tx_test set value='aa' where id=1;       update tx_test set value='aaa' where id=1;
___________________________________________________________________________________________
                                                 ERROR 1205 (HY000): Lock wait timeout exceeded; 
                                                 try restarting transaction
_____________________________________________________________________
                                                
commit;
_____________________________________________________________________

为了防止并发过程当中的修改冲突,事务A中MySQL给id=1的数据行加锁,并一直不commit(释放锁),那么事务B也就一直拿不到该行锁,wait直到超时。数据库

可重复读(Repeatable read)

这是MySQL中InnoDB默认的隔离级别。咱们姑且分“读”和“写”两个模块来说解。安全


读就是可重读,可重读这个概念是一事务的多个实例在并发读取数据时,会看到一样的数据行。session

RC模式下的展示(不可重读)并发

__________________________________________________________________________________________
事务A                                         事务B
__________________________________________________________________________________________
begin;                                         
                                                begin;
__________________________________________________________________________________________
                                               
select * from tx_test;             
______________ 
  id    value 
______________
  1     aa     
______________
  2     b
______________
__________________________________________________________________________________________
                                               update tx_test set value='bb' where id=2;
__________________________________________________________________________________________
                                                
                                               commit;
__________________________________________________________________________________________
select * from tx_test;             
______________
  id    value 
______________
  1     aa     
______________
  2     bb
______________
读到了事务B修改的数据,和第一次查询的结果不同,
是不可重读的。
_________________________________________________________________________________________
commit
_________________________________________________________________________________________

事务B修改id=2的数据提交以后,事务A一样的查询,后一次和前一次的结果不同,这就是不可重读(从新读取产生的结果不同)。这就极可能带来一些问题,那么咱们来看看在RR级别中MySQL的表现:性能

事务A                                         事务B                                             事务C
_________________________________________________________________________________________________________________________________________
begin;                                         
                                                begin;                                              begin;
_________________________________________________________________________________________________________________________________________
                                               
select * from tx_test;             
______________
  id    value 
______________
  1     aa     
______________
  2     bb
______________
___________________________________________________________________________________________________________________________________________
                                               update tx_test set value='aaa' where id=1;          insert into tx_test(value) values('c');
___________________________________________________________________________________________________________________________________________
                                                
                                               commit;                                             commit;
____________________________________________________________________________________________________________________________________________
select * from tx_test;             
______________
  id    value 
______________
  1     aa     
______________
  2     bb
______________
没有读到事务B修改的数据,和第一次sql读取的同样,是可重复读的。

没有读到事务C新添加的数据。
_________________________________________________________________________________________
commit
_________________________________________________________________________________________

咱们注意到,事务A先作了一次读取,事务B中间修改了id=1的数据,并commit以后,事务A第二次读到的数据和第一次彻底相同。因此说它是可重读的。rest

不可重复读和幻读的区别

不可重复读重点在于update和delete,而幻读的重点在于insert。code

若是使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务没法修改这些数据,就能够实现可重复读了。但这种方法却没法锁住insert的数据,因此当事务A先前读取了数据,或者修改了所有数据,事务B仍是能够insert数据提交,这时事务A就会发现莫名其妙多了一条以前没有的数据,这就是幻读,不能经过行锁来避免。须要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么作能够有效的避免幻读、不可重复读、脏读等问题,但会极大的下降数据库的并发能力。

因此说不可重复读和幻读最大的区别,就在于如何经过锁机制来解决他们产生的问题。

上文说的,是使用悲观锁机制来处理这两种问题,可是MySQL、ORACLE、PostgreSQL等成熟的数据库,出于性能考虑,都是使用了以乐观锁为理论基础的MVCC(多版本并发控制)来避免这两种问题。

可串行化(Serializable )

读加共享锁,写加排他锁,读写互斥。使用的悲观锁的理论,实现简单,数据更加安全,可是并发能力很是差。若是你的业务并发的特别少或者没有并发,同时又要求数据及时可靠的话,可使用这种模式。

参考连接:
1.https://tech.meituan.com/innodb-lock.html
2.http://hedengcheng.com/?p=771

相关文章
相关标签/搜索