【译文】MySQL InnoDB 事物模型
InnoDB事物模型
事物的隔離級別
自動提交,提交和回滾
一致的非鎖定讀
鎖定讀
在InnoDB事物模型中,目標是為了多版本數據庫和傳統的倆段鎖協議的最佳實踐(多版本并發控制)。InnoDB在行級別執行鎖行為,并且默認執行數據庫查詢為非鎖定連續讀取。
?
事物的隔離級別:
事物隔離是數據庫中的一個基礎,是ACID中的I原則。事物的隔離級別權衡性能和并發度、可靠性以及可重復可追溯能力的結果。
InnoDB提供了四種隔離級別:未提交讀-RU,提交讀-RC,可重復讀-RR,可序列化-S。InnoDB默認是可重復讀。
InnoDB支持每一個隔離級別,這里描述為不同的鎖策略。你可以使用高度一致性協議(RR),此時在關鍵數據上符合ACID是重要的考慮。也可以降低一致性考慮,使用RC,RU
?
可重復讀 - RR
這是默認的級別。相同事物內一致性讀取,是基于第一次讀取的快照版本。這意味著如果你在同一個事物中執行多個普通的查詢語句,這些查詢語句是一致的。
對于鎖定讀,select ... lock in share mode 或者 select ... for update 或者update或者delete語句,如何鎖定取決與語句是否使用了唯一檢索條件檢索唯一性索引,或者一個范圍檢索條件。
對于唯一條件檢索唯一索引,僅鎖住檢索條件相關的單一索引記錄,并不會包括記錄之前的gap間隙。
對于其他檢索條件,InnoDB鎖住掃描的索引范圍,使用gap lock或者next-key lock來阻塞其他并發事物的插入索引間隙范圍內。
?
提交讀 - RC
每一個讀取操作,甚至是在同一個事物中,都會是數據庫中最新的版本。
對于鎖定讀,select ... lock in share mode 或者 select ... for update 或者update或者delete語句,InnoDB僅鎖住索引記錄,不會鎖住gap,因此它允許自由的插入新紀錄。Gap鎖僅在外鍵約束檢查和重復值檢查時使用。
如果你使用RC,你必須使用row-based的binlog,使用RC還有額外的影響:
對于更新和刪除語句,InnoDB僅持有更新或者刪除的行的鎖,不符合的記錄鎖會在Where條件被解釋后釋放,這能極大的減少死鎖的可能性。
對于更新語句,如果一行已經被鎖住,InnoDB執行"半一致性"讀,返回最近Mysql提交的版本,以便于Mysql可以決定是否符合update語句的Where條件,如果匹配,Mysql再次讀取該行,同時鎖住他或者等待鎖。
舉個例子:
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;這個例子中,表t沒有索引,因此檢索和掃描索引使用隱式的聚簇索引鎖記錄。
假設一個客戶端執行如下Update語句:
SET autocommit = 0; UPDATE t SET b = 5 WHERE b = 3;另外一個客戶端在上述語句執行后執行如下語句:
SET autocommit = 0; UPDATE t SET b = 4 WHERE b = 2;當InnoDB執行每一個Update時,他首先請求獲取X鎖給每一行,然后決定是否修改他。如果InnoDB不修改該行,就釋放鎖。否則,InnoDB會持有鎖直到事物結束。
當使用默認的RR級別時,第一個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第二個update阻塞到獲取任意的鎖,因為第一個update已經在所有的行鎖住,所以第二個update不會執行直到第一個update提交或者回滾。這就是為啥盡量對where子句中出現的字段建立索引的原因。
?
如果是用RC級別,第一個update獲取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)對于第二update,InnoDB做執行“semi-consistent”讀取操作,返回每行的最近提交版本,以便MySQL可以判斷是否該行符合Where篩選條件,不符合的行會釋放鎖。
x-lock(1,2); update(1,2) to (1,4); retain x-lock x-lock(2,3); unlock(2,3) x-lock(3,2); update(3,2) to (3,4); retain x-lock x-lock(4,3); unlock(4,3) x-lock(5,2); update(5,2) to (5,4); retain x-lock?
未提交讀 - RU
序列化 -?SERIALIZABLE
?
自動提交,提交和回滾
如果AutoCommit被啟動,每一條sql語句形成了屬于他自己的事物。MySQL啟動的時候默認開啟會話是啟動AutoCommit的。你可以使用begin,start transaction來開啟一個事物,使用commit或者rollback來提交一個事物。
如果AutoCommit被禁用(SET autocommit = 0),會話總是開啟一個事物,一個commit或者rollback執行結束當前會話時會重新開啟一個事物。如果AutoCommit被禁用,但在會話沒有使用commit提交,MySQL自動回滾rollback。
有一些語句會自動的結束事物,例如數據定義語句(創建表,更改表結構)、使用或者修改MySQL自帶mysql的數據庫、事物控制或者鎖語句(lock/unlock tables),加載數據(load data),管理操作,主從復制控制。
?
一致性非鎖定讀
一致性讀取指:InnoDB使用多版本給query呈現數據庫在某一個時間點的一個快照。Query可以感知到在那個時間點之前事物提交的變化,但是感知不到那個時間點之后的事物或者未提交事物做的變化。
同一事物倆個Query之間夾著修改語句,后一個Query可以感知。這個規則會導致以下的異常:如果你更新了表的某些行,select感知到了更新的變化,但他同樣可以看見其他行的老版本。如果其他會話同時更新相同的表,異常就是意味著你感知到的表的狀態可能從來沒有出現在數據庫中。
如果事物隔離級別是RR,相同事物內的所有的一致性讀,讀取的是第一次讀的數據庫快照,你的Query可以獲取新的快照,通過提交當前的快照同時在之后重新發起一個Query。
注:相同事物內倆個一致性讀取之間夾著一個update,后一個一致性讀取會感知到,即上述注明地方會發生。
如果事物隔離級別是RC,事物內每一個一致性讀都會設置并刷新他自己的快照。
一致性讀是InnoDB默認的,此時InnoDB使用RC, RR級別。一個一致性讀不加任何的鎖,在他訪問的表上。
注:
數據庫狀態的快照只用在select上,如果你插入或者修改了數據庫的某些行,然后提交了,從另外的會話發起的update或者delete,同樣會影響剛剛提交的行,及時會話不能查詢到他們。
如果一個事物更新或者刪除了另外一個事物提交的行,這些變化在當前的會話中是可見的。類似如下:
SELECT COUNT(c1) FROM t1 WHERE c1 = 'xyz'; -- Returns 0: no rows match. DELETE FROM t1 WHERE c1 = 'xyz'; -- Deletes several rows recently committed by other transaction.SELECT COUNT(c2) FROM t1 WHERE c2 = 'abc'; -- Returns 0: no rows match. UPDATE t1 SET c2 = 'cba' WHERE c2 = 'abc'; -- Affects 10 rows: another txn just committed 10 rows with 'abc' values. SELECT COUNT(c2) FROM t1 WHERE c2 = 'cba'; -- Returns 10: this txn can now see the rows it just updated.注:此類現象稱為幻讀,通常發生在一個事物更新,一個事物插入(刪除),更新的時候更新插入的數據,感覺出現了幻覺。
?
如果你想要感知到最新的數據庫狀態(快照),使用RC級別,或者鎖定讀。注:在RR級別盡量不要用鎖定讀,這樣會增加死鎖概率。未經過
?
在不同的select子句的讀類型,INSERT INTO ... SELECT,?UPDATE ... (SELECT), and?CREATE TABLE ... SELECT不指定FOR UPDATE?or?LOCK IN SHARE MODE:
默認地,InnoDB使用強壯的鎖,并且select部分像RC級別一樣執行,此時,每一個一致性讀(甚至是在相同的事物中)會設置和讀取最新的快照。
為了在上述情況使用一致讀,啟用innodb_locks_unsafe_for_binlog(廢棄)選項,并且設置事物的隔離級別為RU,RC,RR(啟用這個選項會影響gap鎖的使用,此時RR和RC很大程度上一致)。這樣,就不會在選中的表中的行讀取時加鎖。
?
鎖定讀
SELECT ... LOCK IN SHARE MODE?設置共享鎖到所有被讀取的行,其他會話可以讀取,但不能修改知道事物提交。如果加共享鎖的這些行被任何其他事物修改。
對于檢索遇到的索引記錄,?SELECT ... FOR UPDATE會鎖住索引記錄以及關聯的索引區間,就像是在這些行上發起一個update請求。
?
轉載于:https://www.cnblogs.com/pengyusong/p/6183369.html
總結
以上是生活随笔為你收集整理的【译文】MySQL InnoDB 事物模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET MVC4 路由的配置
- 下一篇: [Python正则表达式] 字符串中xm