Innodb事務隔離級別

【MySQL】Innodb事務隔離級別(轉)

一、事務隔離級別

ANSI/ISO SQL標準定義了4中事務隔離級別:未提交讀(read uncommitted),提交讀(read committed),重複讀(repeatable read),串行讀(serializable)。

對於不同的事務,採用不同的隔離級別分別有不同的結果。不同的隔離級別有不同的現象。主要有下面3種現在:

1、髒讀(dirty read):一個事務可以讀取另一個尚未提交事務的修改數據。

2、非重複讀(nonrepeatable read):在同一個事務中,同一個查詢在T1時間讀取某一行,在T2時間重新讀取這一行時候,這一行的數據已經發生修改,可能被更新了(update),也可能被刪除了(delete)。

3、幻像讀(phantom read):在同一事務中,同一查詢多次進行時候,由於其他插入操作(insert)的事務提交,導致每次返回不同的結果集。

不同的隔離級別有不同的現象,並有不同的鎖定/併發機制,隔離級別越高,數據庫的併發性就越差,4種事務隔離級別分別表現的現象如下表:

隔離級別 髒讀 非重複讀 幻像讀
read uncommitted 允許 允許 允許
read committed   允許 允許
repeatable read     允許
serializable      

 

InnoDB 中的隔離級詳細描述:

  • READ UNCOMMITTED 這通常稱爲 'dirty read':non-locking SELECTs 的執行使我們不會看到一個記錄的可能更早的版本;因而在這個隔離度下是非 'consistent' reads;另外,這級隔離的運作如同 READ COMMITTED
  • READ COMMITTED 有些類似 Oracle 的隔離級。所有 SELECT ... FOR UPDATE 和 SELECT ... LOCK IN SHARE MODE 語句只鎖定索引記錄,而不鎖定之前的間隙,因而允許在鎖定的記錄後自由地插入新記錄。以一個唯一地搜索條件使用一個唯一索引(unique index)的 UPDATE 和 DELETE,僅僅只鎖定所找到的索引記錄,而不鎖定該索引之前的間隙。但是在範圍型的 UPDATE and DELETE中,InnoDB 必須設置 next-key 或 gap locks 來阻塞其它用戶對範圍內的空隙插入。 自從爲了 MySQL 進行復制(replication)與恢復(recovery)工作'phantom rows'必須被阻塞以來,這就是必須的了。Consistent reads 運作方式與 Oracle 有點類似: 每一個 consistent read,甚至是同一個事務中的,均設置並作用它自己的最新快照。
  • REPEATABLE READ 這是 InnoDB 默認的事務隔離級。. SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODEUPDATE, 和 DELETE ,這些以唯一條件搜索唯一索引的,只鎖定所找到的索引記錄,而不鎖定該索引之前的間隙。 否則這些操作將使用 next-key 鎖定,以 next-key 和 gap locks 鎖定找到的索引範圍,並阻塞其它用戶的新建插入。在 consistent reads 中,與前一個隔離級相比這是一個重要的差別: 在這一級中,同一事務中所有的 consistent reads 均讀取第一次讀取時已確定的快照。這個約定就意味着如果在同一事務中發出幾個無格式(plain)的SELECTs ,這些 SELECTs 的相互關係是一致的。
  • SERIALIZABLE 這一級與上一級相似,只是無格式(plain)的 SELECTs 被隱含地轉換爲 SELECT ... LOCK IN SHARE MODE
InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQLORACLE不同,後者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味着:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!

1).Consistent read 就是 InnoDB 使用它的多版本(multiversioning)方式提供給查詢一個數據庫在一個時間點的快照。

如果以默認的 REPEATABLE READ 隔離級,那麼所有在同一事務中的 consistent reads 只讀取同一個在事務中第一次讀所確定的快照。 你可以通過提交當前事務併發出一個新的查詢以獲得新的數據快照。 Consistent read 對其所訪問的表不加任何鎖定。

(英文原文:

Consistent read is the default mode in which InnoDB processes SELECT statements in READ COMMITTED andREPEATABLE READ isolation levels. A consistent read does not set any locks on the tables it accesses, and therefore other sessions are free to modify those tables at the same time a consistent read is being performed on the table.)

 

 

 

2).Locking reads : (select ...for update與 select ...lock in share mode

http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html 

 

 

Share mode 是共享鎖(寫鎖),允許事務讀取最新數據,如果他被其它事務使用(edit,update)而沒有提交,讀取鎖將被阻塞知道那個事務結束.

For update 排他鎖(讀寫鎖),阻止其他事務讀取或者寫入行數據.

 

SHARE MODE可用來實現參照完整性. 比如有 Parent 和 Child 表, 從 Parent 查出來的信息要插到子表,萬一在插的時候主表數據被刪了怎麼辦?可加 SHARE MODE:

SELECT * FROM PARENT WHERE NAME = 'Jones' LOCK IN SHARE MODE;
	

在共享模式下執行讀取的意思就是讀取最新的現有資料,並在所讀取的行上設置一個共享模式的鎖定。如果最新的數據屬於其它用戶仍未提交的事務,那將不得不等到這個事務被 提交 。共享模式的可以防止其它用戶更新或刪除我們當前所讀取的行。當查詢獲得 'Jones'後,就可以安全地向子表 CHILD 中加入子行,然後提交事務。 

 

 

 

 

二、數據庫中的默認事務隔離級別

在Oracle中默認的事務隔離級別是提交讀(read committed)。

對於MySQL的Innodb的默認事務隔離級別是重複讀(repeatable read。可以通過下面的命令查看:

mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation;

+———————–+—————–+

| @@GLOBAL.tx_isolation | @@tx_isolation  |

+———————–+—————–+

REPEATABLE-READ | REPEATABLE-READ |

+———————–+—————–+

1 row in set (0.00 sec)

用戶也可以通過下面的 SQL 語句爲單個連接或所有新建的連接改變隔離級:

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL
                       {READ UNCOMMITTED | READ COMMITTED
                           | REPEATABLE READ | SERIALIZABLE}
 

 

下面進行一下測試:

Time Session 1 Session 2
T1 set autocommit=0; set autocommit=0;
T2 mysql> select * from tmp_test;

+——+———+
| id   | version |
+——+———+
|    1 |       1 |
+——+———+

1 row in set (0.00 sec)

 
T3   mysql> update tmp_test set version=2 where id=1;

Query OK, 1 row affected (0.02 sec)

Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from tmp_test;

+——+———+
| id   | version |
+——+———+
|    1 |       2 |
+——+———+

1 row in set (0.00 sec)

T4 mysql> select * from tmp_test;

+——+———+
| id   | version |
+——+———+
|    1 |       1 |
+——+———+

1 row in set (0.00 sec)

【說明】
Session 2未提交,看到數據不變,無髒讀。

 
T5   commit;
T6 mysql> select * from tmp_test;

+——+———+
| id   | version |
+——+———+
|    1 |       1 |
+——+———+

1 row in set (0.00 sec)

【說明】
Session 2已經提交,還是看到數據不變,即可以重複讀。

 
T7 commit;  
T8 mysql> select * from tmp_test;

+——+———+
| id   | version |
+——+———+
|    1 |       2 |
+——+———+

1 row in set (0.00 sec)

【說明】
提交事務,看到最新數據。

 
T9   mysql> insert into tmp_test values(2,1);

Query OK, 1 row affected (0.00 sec)

mysql> select * from tmp_test;

+——+———+
| id   | version |
+——+———+
|    1 |       2 |
|    2 |       1 |
+——+———+

2 rows in set (0.00 sec)

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

T10 mysql> select * from tmp_test;

+——+———+
| id   | version |
+——+———+
|    1 |       2 |
+——+———+

1 row in set (0.00 sec)

【說明】
Session 2的insert事務已經提交,看到的數據和T8的時候一樣,即未發生幻象讀。

 
T11 mysql> commit;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from tmp_test;

+——+———+
| id   | version |
+——+———+
|    1 |       2 |
|    2 |       1 |
+——+———+

2 rows in set (0.00 sec)

【說明】
事務提交,看到最新數據。

 

上面的結果可以看到Innodb的重複讀(repeatable read)不允許髒讀,不允許非重複讀(即可以重複讀,Innodb使用多版本一致性讀來實現)和不允許幻象讀(這點和ANSI/ISO SQL標準定義的有所區別)。

另外,同樣的測試:

1、當session 2進行truncate表的時候,這個時候session 1再次查詢就看不到數據。

2、當session 2進行alter表的時候,這個時候session 1再次查詢就看不到數據。

MySQL官方文檔中的多版本一致性讀中說明了原因:Consistent read does not work over certain DDL statements。

關於Innodb的多版本一致性讀將下一篇再來記錄。


發佈了20 篇原創文章 · 獲贊 6 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章