InnoDB 事務加鎖分析
本文首發於 vivo網際網路技術 微信公眾號
連結: https://mp.weixin.qq.com/s/S7MhlsZveBHRSQhq5aTIJA
作者:何志創
一般大家對資料庫事務的瞭解可能停留在事務的ACID特性以及事務4種不同的隔離級別層面上,而對於事務 4 種不同隔離級別如何實現瞭解相對較少。
本文以 MySQL 資料庫 InnoDB 引擎為例,為大家分析 InnoDB資料庫引擎對預設的隔離級別可重複讀(RR)的具體實現。
整文知識點介紹:事務4種隔離級別、不同隔離級別解決的問題、MVCC、鎖的型別、加鎖案例分析;閱讀完整文相信大家對事務隔離級別的具體實現有了一定的認識。
一、事務的隔離級別
1、4 種隔離級別
(1)未提交讀(Read uncommitted):一個事務讀取到其他事務未提交的資料,是級別最低的隔離機制;
(2)提交讀(Read committed):一個事務讀取到其他事務提交後的資料;
(3)可重複讀(Repeatable read):一個事務對同一份資料讀取到的相同,不在乎其他事務對資料的修改;
(4)序列化(Serializable) :事務序列化執行,隔離級別最高,犧牲了系統的併發性。
2、不同隔離級別解決的問題
若不考慮事務的隔離級別,則事務的併發會造成以下問題:
(1)髒讀:事務A讀取了事務B更新的資料,然後B回滾操作,那麼A讀取到的資料是髒資料。
(2)不可重複讀:事務 A 多次讀取同一資料,事務 B 在事務A多次讀取的過程中,對資料作了更新並提交,導致事務A多次讀取同一資料時,結果 不一致。
(3)幻讀:同一事務中對同一範圍的資料進行讀取,結果卻多出了資料或者少了資料,這就叫幻讀。(如同一事務對id<10的範圍進行2次查詢,第一次出現id=8、9的兩條資料,第二次出現id=7、8、9的3條資料)。
不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。
不同的隔離級別針對上述3個問題的解決能力,如下表:
二、MVCC
上文提到 InnoDB 預設的隔離級別是可重複讀(RR),InnoDB是透過MVCC(多版本併發控制)來實現可重複讀的,下面為大家介紹MVCC。
1、概念
在InnoDB中,給每行增加兩個隱藏欄位來實現MVCC,一個用來記錄資料行的建立時間,另一個用來記錄行的過期時間(刪除時間)。在實際操作中,儲存的並不是時間,而是事務的版本號,每開啟一個新事務,事務的版本號就會遞增。
於是乎,預設的隔離級別(REPEATABLE READ)下,增刪查改變成了這樣:
(1)SELECT
-
讀取建立版本小於或等於當前事務版本號,並且刪除版本為空或大於當前事務版本號的記錄。這樣可以保證在讀取之前記錄是存在的。
(2)INSERT
-
將當前事務的版本號儲存至行的建立版本號。
(3)UPDATE
-
新插入一行,並以當前事務的版本號作為新行的建立版本號,同時將原記錄行的刪除版本號設定為當前事務版本號。
(4)DELETE
-
將當前事務的版本號儲存至行的刪除版本號。
2、快照讀和當前讀
(1)快照讀:讀取的是快照版本,也就是歷史版本;
(2)當前讀:讀取的是最新版本。
普通的SELECT就是快照讀,而UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE是當前讀。
(3)結論:如果隔離級別是REPEATABLE READ,那麼在同一個事務中的所有普通select讀讀到的都是事務第一個讀到的快照,如此實現了可重複讀;而對於當前讀(UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE),InnoDB 透過加鎖來實現可重複讀,且InnoDB 加鎖同時解決了幻讀問題。
三、鎖的型別
InnoDB 引入以下三種鎖型別:
-
Record Locks(記錄鎖):在索引記錄上加鎖,即行鎖,鎖住當前行。
-
Gap Locks(間隙鎖):在索引記錄之間加鎖,或者在第一個索引記錄之前加鎖,或者在最後一個索引記錄之後加鎖。
-
Next-Key Locks:在索引記錄上加鎖,並且在索引記錄之前的間隙加鎖。它相當於是Record Locks與Gap Locks的一個結合。
假設一個索引包含以下幾個值:10,11,13,20。那麼這個索引的next-key鎖將會覆蓋以下區間:(-oo, 10]、(10, 11]、(11, 13]、(13, 20]、(20, +oo)。
MySQL InnoDB 透過間隙鎖解決了幻讀問題。以下透過實際的案例分析來介紹InnoDB 是如果解決幻讀問題的。
四、案例分析
在對SQL進行加鎖分析前,需要明確表的結構和索引型別。在不知道索引的情況下直接給出一條SQL來分析如果加鎖是沒有任何意義的。
以下以使用者表(t_user)為例( id為主鍵,name為唯一索引,age為一般索引,address無索引)分析不同索引條件的加鎖表現。
1、主鍵索引
例:delete from t_user where id=120;
條件為主鍵,此時鎖住聚簇索引中對應的行記錄:即Record Locks鎖住id=120的行記錄。
此種情況下,其他事務除了不能刪除、更新此條記錄外,其他插入其他行、更新其他行都行。
SQL驗證:
2、唯一索引
例:delete from t_user where name='n20';
條件為唯一索引,鎖住索引記錄,同時鎖住聚簇索引中的對應行記錄:
SQL驗證:
3、一般索引
例:delete from t_user where age=20;
與主鍵和唯一索引不同的是,一般索引的記錄是允許重複的;換句話說,如果我們單純地給索引加記錄鎖時,其他事務依然可以插入,也就有可能出現幻讀問題了。
所以 除了給對應索引記錄加上記錄鎖之外,還要給Gap加上鎖。
從上面知識點我們可以預估這個操作一共需要的鎖:
-
age索引記錄鎖(Record Lock) :
20_120, 20_130(以下均用age_id這種形式表示索引值)
-
age索引間隙鎖(Gap X-Lock):
(10, 20)、(20, 20)、(20, 40)
-
聚簇索引上的記錄鎖(Record X-Lock):
id=120/130對應的行記錄
SQL驗證:
根據實際情況,3-6均符合我們預期,然而7和8則超出了我們預期的鎖範圍。為什麼會超出我們預期呢?此次我們進行分析一下:
從7、8插入語句來看,由於id為自增主鍵,會自動遞增,語句7插入值預計為:10_141;
語句8插入值預計為:40_141,為什麼只有後者能插入呢?
其實我們可以將B+樹中的間隙理解得更加
精準一點:
age=20的三個間隙應該為:(10_110, 20_120)、(20_120, 20_130)、(20_130, 40_140);
從上圖可以看出語句7插入值10_141 無法插入,因為間隙被鎖住了;而語句8插入 40_141值因為在間隙之外了,無鎖衝突,允許插入。
所以最終的加鎖情況應該這樣表示:
-
age索引記錄鎖(Record Lock) :20_120, 20_130
-
age索引間隙鎖(Gap X-Lock):(10_110, 20_120)、(20_120, 20_130)、(20_130, 40_140)
-
聚簇索引上的記錄鎖(Record X-Lock):id=120/130對應的行記錄
4、無索引
delete from t_user where address='a20',因為無法精準定位,InnoDB選擇將聚簇索引中的所有行以及間隙都鎖起來,功能上已經等於鎖表了:
SQL驗證:
5、結論
InnoDB 在RC(READ COMMITTED)隔離級別中,只會在對應的索引/行記錄上加Record Lock,而不會加Gap鎖,原因也很簡單,因為該隔離級別是允許存在幻讀問題的。
在RR級別下的加鎖方式稱之為Next-Key Locks,其實就是上述Record Locks和Gap Locks的結合。比如Gap Lock為(10,20) ,record lock為20,結合的Next-Key lock 為:(10, 20]。
分析Next-Key Locks其實就是要分析Record Locks和Gap Locks。MySQL InnoDB的可重複讀並不保證避免幻讀,需要應用使用加鎖讀來保證。而這個加鎖讀使用到的機制就是next-key locks。
如果使用普通的讀,會得到一致性的結果,如果使用了加鎖的讀,就會讀到“最新的”“提交”讀的結果。本身,可重複讀和提交讀是矛盾的。在同一個事務裡,如果保證了可重複讀,
就會看不到其他事務的提交,違背了提交讀;如果保證了提交讀,就會導致前後兩次讀到的結果不一致,違背了可重複讀。可以這麼講,InnoDB提供了這樣的機制,在預設的可重複讀的隔離級別裡,可以使用加鎖讀去查詢最新的資料。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912579/viewspace-2673644/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MyRocks事務鎖分析
- 事務隔離(二):基於加鎖方式的事務隔離原理
- For Update 加鎖分析
- Mysql innodb引擎(三) 事務MySql
- 設定mysql 事務鎖超時時間 innodb_lock_wait_timeoutMySqlAI
- MySQL InnoDB中的事務隔離級別和鎖的關係MySql
- 十、Redis事務、事務鎖Redis
- MySql(四) InnoDB事務淺析MySql
- 從ReentrantLock加鎖解鎖角度分析AQSReentrantLockAQS
- MySQL探祕(八):InnoDB的事務MySql
- 四、InnoDB儲存引擎如何利用鎖實現四種事務隔離級別儲存引擎
- InnoDB 層鎖、事務、統計資訊字典表 | 全方位認識 information_schemaORM
- MySQL的事務機制和鎖(InnoDB引擎、MVCC多版本併發控制技術)MySqlMVC
- MySQL事務與鎖MySql
- MySQL 事務和鎖MySql
- MySQL事務和鎖MySql
- MySql 三大知識點,索引、鎖、事務,原理分析MySql索引
- 搞懂MySQL InnoDB事務ACID實現原理MySql
- MySQL死鎖系列-常見加鎖場景分析MySql
- MS SQL Server資料庫事務鎖機制分析(轉)SQLServer資料庫
- MySQL 筆記 - 事務&鎖MySql筆記
- mysql之鎖與事務MySql
- redis-19.事務-鎖Redis
- MySQL資料庫事務各隔離級別加鎖情況--read uncommittMySql資料庫MIT
- MySQL資料庫事務各隔離級別加鎖情況--Repeatable ReaMySql資料庫
- Innodb中有哪些鎖?
- MySQL innodb引擎的事務執行過程MySql
- InnoDB意向鎖和插入意向鎖
- MySQL鎖:03.InnoDB行鎖MySql
- 關於資料庫事務和鎖的一些分析資料庫
- 一次ORACLE分散式事務鎖異常處理分析Oracle分散式
- 故障分析 | 從 Insert 併發死鎖分析 Insert 加鎖原始碼邏輯原始碼
- 驚!史上最全的select加鎖分析(Mysql)MySql
- 超全面 MySQL 語句加鎖分析(上篇)MySql
- 超全面 MySQL 語句加鎖分析(中篇)MySql
- 超全面 MySQL 語句加鎖分析(下篇)MySql
- etcd分散式鎖及事務分散式
- MySQL入門--事務與鎖MySql