隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
未提交读(Read uncommitted) | 可能 | 可能 | 可能 |
已提交读(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
数据库通常都不会用,并且任何操做都不会加锁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; _____________________________________________________________________
在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直到超时。数据库
这是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(多版本并发控制)来避免这两种问题。
读加共享锁,写加排他锁,读写互斥。使用的悲观锁的理论,实现简单,数据更加安全,可是并发能力很是差。若是你的业务并发的特别少或者没有并发,同时又要求数据及时可靠的话,可使用这种模式。
参考连接:
1.https://tech.meituan.com/innodb-lock.html
2.http://hedengcheng.com/?p=771