【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-lockingSELECT
s 的執行使我們不會看到一個記錄的可能更早的版本;因而在這個隔離度下是非 'consistent' reads;另外,這級隔離的運作如同READ COMMITTED
。READ COMMITTED
有些類似 Oracle 的隔離級。所有SELECT ... FOR UPDATE
和SELECT ... LOCK IN SHARE MODE
語句只鎖定索引記錄,而不鎖定之前的間隙,因而允許在鎖定的記錄後自由地插入新記錄。以一個唯一地搜索條件使用一個唯一索引(unique index)的UPDATE
和DELETE
,僅僅只鎖定所找到的索引記錄,而不鎖定該索引之前的間隙。但是在範圍型的UPDATE
andDELETE
中,InnoDB 必須設置 next-key 或 gap locks 來阻塞其它用戶對範圍內的空隙插入。 自從爲了 MySQL 進行復制(replication)與恢復(recovery)工作'phantom rows'必須被阻塞以來,這就是必須的了。Consistent reads 運作方式與 Oracle 有點類似: 每一個 consistent read,甚至是同一個事務中的,均設置並作用它自己的最新快照。REPEATABLE READ
這是 InnoDB 默認的事務隔離級。.SELECT ... FOR UPDATE
,SELECT ... LOCK IN SHARE MODE
,UPDATE
, 和DELETE
,這些以唯一條件搜索唯一索引的,只鎖定所找到的索引記錄,而不鎖定該索引之前的間隙。 否則這些操作將使用 next-key 鎖定,以 next-key 和 gap locks 鎖定找到的索引範圍,並阻塞其它用戶的新建插入。在 consistent reads 中,與前一個隔離級相比這是一個重要的差別: 在這一級中,同一事務中所有的 consistent reads 均讀取第一次讀取時已確定的快照。這個約定就意味着如果在同一事務中發出幾個無格式(plain)的SELECT
s ,這些SELECT
s 的相互關係是一致的。SERIALIZABLE
這一級與上一級相似,只是無格式(plain)的SELECT
s 被隱含地轉換爲SELECT ... LOCK IN SHARE MODE
。
1).Consistent read 就是 InnoDB 使用它的多版本(multiversioning)方式提供給查詢一個數據庫在一個時間點的快照。
如果以默認的 REPEATABLE READ
隔離級,那麼所有在同一事務中的 consistent reads 只讀取同一個在事務中第一次讀所確定的快照。 你可以通過提交當前事務併發出一個新的查詢以獲得新的數據快照。 Consistent
read 對其所訪問的表不加任何鎖定。
(英文原文:
Consistent read is the default mode in whichInnoDB
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;
+——+———+ 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;
+——+———+ 1 row in set (0.00 sec) |
|
T4 |
mysql> select * from tmp_test;
+——+———+ 1 row in set (0.00 sec)
【說明】 |
|
T5 | commit; | |
T6 |
mysql> select * from tmp_test;
+——+———+ 1 row in set (0.00 sec)
【說明】 |
|
T7 | commit; | |
T8 |
mysql> select * from tmp_test;
+——+———+ 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;
+——+———+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) |
|
T10 |
mysql> select * from tmp_test;
+——+———+ 1 row in set (0.00 sec)
【說明】 |
|
T11 |
mysql> commit;
Query OK, 0 rows affected (0.00 sec) mysql> select * from tmp_test;
+——+———+ 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的多版本一致性讀將下一篇再來記錄。