隔離級別是在多個事務同時進行更改和執行查詢時,對性能與結果的可靠性、一致性和再現性之間的平衡進行微調的設定,
提供了SQL:1992標準中描述的四種事務隔離級別:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE,InnoDB默認的隔離級別是REPEATABLE READ,
InnoDB使用不同的鎖定策略支持這里描述的每個事務隔離級別,下面的串列描述了MySQL如何支持不同的事務級別,
1. REPEATABLE READ(可重復讀)
這是InnoDB的默認隔離級別,同一個事務中的一致性讀取讀的是第一次讀取時建立的快照,這意味著,如果在同一個事務中發出幾個普通(非鎖定)SELECT陳述句,這些SELECT陳述句彼此之間也是一致的,(PS:可重復讀這個級別就是要保證同一個事務中,多次讀取相同的資料,回傳的結果是一樣的,)
對于鎖定讀(SELECT with FOR UPDATE or LOCK IN SHARE MODE)、UPDATE 和 DELETE 陳述句,鎖定取決于陳述句是使用具有唯一搜索條件的唯一索引,還是使用范圍型別的搜索條件,
- 對于具有唯一搜索條件的唯一索引,InnoDB只鎖定找到的索引記錄,而不鎖定之間的間隙,
- 對于其他搜索條件,InnoDB鎖定掃描到的索引范圍,使用間隙鎖或next-key鎖以阻止其他會話向該范圍覆寫的間隙中插入,
2. READ COMMITTED(讀已提交)
即使在同一事務中,每個一致的讀取都將設定并讀取自己的新快照,(PS:對比一下,對于一致性讀,可重復讀是讀取事務中第一次讀取時候建立的快照;而讀已提交,每次都是讀最新的快照)
對于鎖讀(SELECT with For UPDATE or LOCK IN SHARE MODE)、UPDATE陳述句和DELETE陳述句,InnoDB只鎖住索引記錄,而不鎖住它們之間的間隙,從而允許在鎖住的記錄旁邊自由插入新記錄,間隙鎖定僅用于外鍵約束檢查和重復鍵檢查,
由于禁用了間隙鎖,可能會出現幻像問題,因為其他會話可以在間隙中插入新行,
使用READ COMMITTED具有額外的效果:
- 對于UPDATE或DELETE陳述句,InnoDB僅對其更新或洗掉的行持有鎖,MySQL評估WHERE條件后,將釋放不匹配行的記錄鎖,這大大降低了死鎖的可能性,但是仍然可能發生,
- 對于UPDATE陳述句,如果某行已被鎖定,則InnoDB執行“半一致”讀取,將最新的提交版本回傳給MySQL,以便MySQL可以確定該行是否與UPDATE的WHERE條件匹配,如果該行匹配(必須更新),則MySQL會再次讀取該行,這一次InnoDB將其鎖定或等待對其進行鎖定,
舉個例子,考慮下面的陳述句:
CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB; INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2); COMMIT;
在這種情況下,表沒有索引,因此搜索和索引掃描使用隱藏的聚集索引鎖定記錄
假設,第一個會話要執行update陳述句:
# Session A START TRANSACTION; UPDATE t SET b = 5 WHERE b = 3;
假設,在第一個會話之后,第二個會話也要執行如下update陳述句:
# Session B UPDATE t SET b = 4 WHERE b = 2;
當InnoDB執行每個UPDATE時,它首先為讀取的每一行獲取一個排他鎖,然后決定是否修改它,如果InnoDB沒有修改該行,則釋放鎖,否則,InnoDB會保留鎖直到事務結束,這對事務處理的影響如下,
如果用默認的REPEATABLE READ隔離級別,第一個UPDATE在其讀取的每一行上獲取一個X鎖,并且不釋放其中的任何一個:
x-lock(1,2); retain x-lock x-lock(2,3); update(2,3) to (2,5); retain x-lock x-lock(3,2); retain x-lock x-lock(4,3); update(4,3) to (4,5); retain x-lock x-lock(5,2); retain x-lock
(PS:之所以每一行都加排它鎖,是因為b上沒有唯一索引)
第二個UPDATE在嘗試獲取任何鎖時立即阻塞(因為第一個UPDATE已在所有行上保留了鎖),并且直到第一個UPDATE提交或回滾后才繼續進行:
x-lock(1,2); block and wait for first UPDATE to commit or roll back
如果用READ COMMITTED隔離級別,第一個UPDATE在其讀取的每一行上獲取一個X鎖,并為未修改的行釋放X鎖:
x-lock(1,2); unlock(1,2) x-lock(2,3); update(2,3) to (2,5); retain x-lock x-lock(3,2); unlock(3,2) x-lock(4,3); update(4,3) to (4,5); retain x-lock x-lock(5,2); unlock(5,2)
但是,如果WHERE條件包含一個索引列,并且InnoDB使用該索引,那么在獲取和保留記錄鎖時,只考慮索引列,
再看下面一個例子,第一個UPDATE在b = 2的每一行上獲取并保留一個X鎖,第二個UPDATE在嘗試獲取同一記錄上的X鎖時會阻塞,因為它也使用在b列上定義的索引 ,
CREATE TABLE t (a INT NOT NULL, b INT, c INT, INDEX (b)) ENGINE = InnoDB; INSERT INTO t VALUES (1,2,3),(2,2,4); COMMIT; # Session A START TRANSACTION; UPDATE t SET b = 3 WHERE b = 2 AND c = 3; # Session B UPDATE t SET b = 4 WHERE b = 2 AND c = 4;
(PS:b列上有索引,這兩條陳述句在檢索時使用了b列上的索引,因此第一會話在b=2的列上加了X鎖,第一個會話更新b=2時會阻塞)
3. READ UNCOMMITTED(讀未提交)
會出現臟讀,一般不使用
4. SERIALIZABLE(串行化)
此級別類似于REPEATABLE READ,但如果禁用了自動提交,則InnoDB會將所有普通的SELECT陳述句隱式轉換為SELECT ... LOCK IN SHARE MODE,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/253456.html
標籤:MySQL
下一篇:MySQL 幻象行
