InnoDB鎖機制

来源:http://www.cnblogs.com/butterfly100/archive/2017/11/13/7827211.html
-Advertisement-
Play Games

1. 鎖類型 鎖是資料庫區別與文件系統的一個關鍵特性,鎖機制用於管理對共用資源的併發訪問。 InnoDB使用的鎖類型,分別有: 共用鎖(S)和排他鎖(X) 意向鎖(IS和IX) 自增長鎖(AUTO INC Locks) 1.1. 共用鎖和排他鎖 InnoDB實現了兩種標準的行級鎖:共用鎖(S)和排他 ...


1. 鎖類型

鎖是資料庫區別與文件系統的一個關鍵特性,鎖機制用於管理對共用資源的併發訪問。
InnoDB使用的鎖類型,分別有:

  • 共用鎖(S)和排他鎖(X)
  • 意向鎖(IS和IX)
  • 自增長鎖(AUTO-INC Locks)

1.1. 共用鎖和排他鎖

InnoDB實現了兩種標準的行級鎖:共用鎖(S)和排他鎖(X)

共用鎖:允許持有該鎖的事務讀取行記錄。如果事務 T1 擁有記錄 r 的 S 鎖,事務 T2 對記錄 r 加鎖請求:若想要加 S 鎖,能馬上獲得;若想要獲得 X 鎖,則請求會阻塞。

排他鎖:允許持有該鎖的事務更新或刪除行記錄。如果事務 T1 擁有記錄 r 的 X 鎖,事務 T2 對記錄 r 加鎖請求:無論想獲取 r 的 S 鎖或 X 鎖都會被阻塞。

S 鎖和 X 鎖都是行級鎖。

1.2. 意向鎖

InnoDB 支持多粒度的鎖,允許一行記錄同時持有相容的行鎖和表鎖。意向鎖是表級鎖,表明一個事務之後要獲取表中某些行的 S 鎖或 X 鎖。

InnoDB中使用了兩種意向鎖

  • 意向共用鎖(IS):事務 T 想要對錶 t 中的某些記錄加上 S 鎖
  • 意向排他鎖(IX):事務 T 想要對錶 t 中的某些記錄加上 X 鎖

例如:

  • SELECT ... LOCK IN SHARE MODE,設置了 IS 鎖
  • SELECT ... FOR UPDATE,設置了 IX 鎖

意向鎖協議如下所示:

  • 在一個事務對錶 t 中某一記錄 r 加 S 鎖之前,他必須先獲取表 t 的 IS 鎖
  • 在一個事務對錶 t 中某一記錄 r 加 X 鎖之前,他必須先獲取表 t 的 IX 鎖

這些規則可以總結為下麵的圖表(橫向表示一個事務已經獲取了對應的鎖,縱向表示另外一個事務想要獲取對應的鎖):

IX,IS是表級鎖,不會和行級的X,S鎖發生衝突。只會和表級的X,S發生衝突

X IX S IS
X 不相容 不相容 不相容 不相容
IX 不相容 相容 不相容 相容
S 不相容 不相容 相容 相容
IS 不相容 相容 相容 相容

當請求的鎖與已持有的鎖相容時,則加鎖成功;如果衝突的話,事務將會等待已有的衝突的鎖釋放

IX 和 IS 鎖的主要目的是表明:某個請求正在或者將要鎖定一行記錄。意向鎖的作用:意向鎖是在添加行鎖之前添加。當再向一個表添加表級 X 鎖的時候

  • 如果沒有意向鎖的話,則需要遍歷所有整個表判斷是否有行鎖的存在,以免發生衝突
  • 如果有了意向鎖,只需要判斷該意向鎖與即將添加的表級鎖是否相容即可。因為意向鎖的存在代表了,有行級鎖的存在或者即將有行級鎖的存在。因而無需遍歷整個表,即可獲取結果

意向鎖使用 SHOW ENGINE INNODB STATUS 查看當前鎖請求的信息:

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

1.3. 自增長鎖

InnoDB中,對每個含有自增長值的表都有一個自增長計數器(aito-increment counter)。當對含有自增長計數器的表進行插入操作時,這個計數器會被初始化。執行如下語句會獲得自增長的值

SELECT MAX(auto_inc_col) FROM t FOR UPDATE;

插入操作會依據這個自增長的計數器值加1賦予到自增長列。這種實現方式是AUTO_INC Locking。這種鎖採用了一種特殊的表鎖機制,為提高插入的性能,鎖不是在一個事務完成後釋放,而是在完成對自增長值插入的SQL語句後立即釋放。雖然AUTO-INC Locking一定方式提升了併發插入的效率,但還是存在性能上的一些問題:

  • 首先,對自增長值的列併發插入性能較差,事務必須等待前一個插入SQL的完成
  • 其次,對於 insert... select 的大數據量插入會影響插入的性能,因為另一個插入的事務會被阻塞

InnoDB提供了一種輕量級互斥量的自增長實現機制,大大提高了自增長值插入的性能。提供參數innodb_autoinc_lock_mode來控制自增長鎖使用的演算法,預設值為1。他允許你在可預測的自增長值和最大化併發插入操作之間進行權衡。

插入類型的分類:

插入類型 說明
insert-like 指所有的插入語句,例如:insert、replace、insert ... select、replace... select、load data
simple inserts 指再插入前就確定插入行數的語句。例如:insert、replace等。註意:simple inserts不包含 insert ... on duplicate key update 這類sql語句
bulk inserts 指在插入前不能確定得到插入行數的語句,例如:insert ... select、 replace ... select、load data
mixed-mode inserts 指插入中有一部分的值是自增長的,一部分是確定的。例如:insert into t1(c1, c2) values (1, 'a'), (NULL, 'b'), (5, 'c'), (NULL,'d'); 也可以指 insert ... on duplicate key update 這類sql語句

innodb_autoinc_lock_mode 在不同設置下對自增長的影響:

innodb_autoinc_lock_mode = 0

MySQL 5.1.22版本之前自增長的實現方式,通過表鎖的AUTO-INC Locking方式

innodb_autoinc_lock_mode = 1(預設值)

對於『simple inserts』,該值會用互斥量(mutex)對記憶體中的計數器進行累加操作。對於『bulk inserts』會用傳統的AUTO-INC Locking方式。這種配置下,如果不考慮回滾,自增長列的增長還是連續的。需要註意的是:如果已經使用AUTO-INC Locking方式去產生自增長的值,而此時需要『simple inserts』操作時,還需要等待AUTO-INC Locking的釋放

innodb_autoinc_lock_mode = 2

對於所有『insert-like』自增長的產生都是通過互斥量,而不是AUTO-INC Locking方式。這是性能最高的方式。但會帶來一些問題:

  • 因為併發插入的存在,每次插入時,自增長的值是不連續的
  • 基於statement-base replication會出現問題

因此,使用這種方式,任何情況下都需要使用row-base replication,這樣才能保證最大併發性能和replication的主從數據的一致 |

2. 鎖的演算法

InnoDB存儲引擎行鎖的演算法

  • Record Locks:單個行記錄上的鎖
  • Gap Locks:間隙鎖,鎖定一個範圍,不包含記錄本身
  • Next-Key Locking:Record Locks + Gap Locks,鎖住一個範圍 + 記錄本身
  • Insert Intention Locks:插入易向鎖

2.1. 行鎖

行鎖是加在索引記錄上的鎖,例如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE,會阻止其他事務插入、更新或刪除 t.c1 = 10 的記錄

行鎖總是在索引記錄上面加鎖,即使一張表沒有設置任何索引,InnoDB會創建一個隱藏的聚簇索引,然後在這個索引上加上行鎖。

行鎖使用 SHOW ENGINE INNODB STATUS 的輸出如下:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
 
trx id 10078 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

2.2. 間隙鎖

間隙鎖是加在索引記錄間隙之間的鎖,或者在第一條索引記錄之前、最後一條索引記錄之後的區間上加的鎖。例如:SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 這條語句阻止其他的事務插入一條 t.c1 = 15 的記錄,因為在10-20的範圍值都已經被加上了鎖。

間隙鎖只在RR隔離級別中使用。如果一條sql使用了唯一索引(包括主鍵索引),那麼不會使用到間隙鎖

例如:id 列是唯一索引,下麵的語句只會在 id = 100 行上面使用Record Lock,而不會關心別的事務是否在上述的間隙中插入數據。如果 id 列沒有索引或者不是唯一索引,這個語句會在上述的間隙上加鎖。

SELECT * FROM child WHERE id = 100 FOR UPDATE;

2.3. Next-Key鎖

Next-Key Lock是結合了Gap Lock 和 Record Lock的一種鎖演算法。

當掃描表的索引時,InnoDB以這種形式實現行級的鎖:遇到匹配的的索引記錄,在上面加上對應的 S 鎖或 X 鎖。因此,行級鎖實際上是索引記錄鎖。如果一個事務擁有索引上記錄 r 的一個 S 鎖或 X 鎖,另外的事務無法立即在 r 記錄索引順序之前的間隙上插入一條新的記錄。

假設有一個索引包含值:10,11,13和20。下列的間隔上都可能加上一個Next-Key 鎖(左開右閉)

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

在最後一個區間中,Next-Key鎖 鎖定了索引中的最大值到 正無窮。

預設情況下,InnoDB啟用 RR 事務隔離級別。此時,InnoDB在查找和掃描索引時會使用 Next-Key 鎖,其設計的目的是為瞭解決『幻讀』的出現。

當查詢的列是唯一索引情況下,InnoDB會對Next-Key Lock進行優化,降級為Record Lock,即只鎖住索引本身,而不是範圍。

next-key 鎖 使用 SHOW ENGINE INNODB STATUS 輸出如下:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10080 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;
 
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 6; hex 00000000274f; asc     'O;;
 2: len 7; hex b60000019d0110; asc        ;;

2.4. 插入意向鎖

插入意向鎖是一種在數據行插入前設置的gap鎖。這種鎖用於在多事務插入同一索引間隙時,如果這些事務不是往這段gap的同一位置插入數據,那麼就不用互相等待。假如有4和7兩個索引記錄值。不同的事務嘗試插入5和6的值。在不同事務獲取分別的 X 鎖之前,他們都獲得了4到7範圍的插入意向鎖,但是他們無需互相等待,因為5和6這兩行不衝突。

例如:客戶端A和B,在插入記錄獲取互斥鎖之前,事務正在獲取插入意向鎖。

客戶端A創建了一個表,包含90和102兩條索引記錄,然後去設置一個互斥鎖在大於100的所有索引記錄上。這個互斥鎖包含了在102記錄前的gap鎖。

mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);
 
mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+

客戶端B 開啟一個事務在這段gap上插入新紀錄,這個事務在等待獲取互斥鎖之前,獲取了一把插入意向鎖。

mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

插入意向鎖 使用 SHOW ENGINE INNODB STATUS 輸出如下:

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000066; asc    f;;
 1: len 6; hex 000000002215; asc     " ;;
 2: len 7; hex 9000000172011c; asc     r  ;;...

3. SQL加鎖分析

給定兩個SQL來分析InnoDB下加鎖的過程:

SQL1:select * from t1 where id = 10;

SQL2:delete * from t1 where id = 10;

事務隔離級別為預設隔離級別Repeatable Read。而對於id不同的索引類型,會有不同的結論。(總結自何登成大神的 MySQL 加鎖處理分析

SQL1:在RC和RR下,因為MVCC併發控制,select操作不需要加鎖,採用快照讀。讀取記錄的可見版本(可能是歷史版本)

針對SQL2:如下分不同情況

3.1. id主鍵

將主鍵上,id=10的記錄加上 X 鎖

3.2. id唯一索引

id不是主鍵,而是一個唯一的二級索引,主鍵是name列。加鎖步驟如下:

  1. 會選擇走id列的索引進行where條件的過濾。找到id=10的記錄後,首先將唯一索引上id=10的索引記錄加上 X 鎖
  2. 同時,根據讀取到的name列回主鍵索引(聚簇索引),然後將聚簇索引上的 name='d' 對應的主鍵索引記錄添加 X 鎖

聚簇索引加鎖的原因:如果併發的一個SQL是通過主鍵索引來更新:update t1 set id = 100 where name = 'd'; 此時,如果delete語句沒有將主鍵索引上的記錄加鎖,那麼併發的update就會感知不到delete語句的存在。違背同一條記錄的更新/刪除需要串列執行的約束。

3.3. id非唯一索引

加鎖步驟如下:

  1. 通過id索引定位到第一條滿足條件的記錄,加上 X 鎖
  2. 這條記錄的間隙上加上 GAP鎖
  3. 根據讀取到的name列回主鍵聚簇索引,對應記錄加上 X 鎖
  4. 返回讀取下一條,重覆進行... 直到第一條不滿足 where id = 10 條件的記錄 [11, f],此時不需要加 X 鎖,仍舊需要加 GAP 鎖。結束返回

幻讀解決:
這幅圖中多了個GAP鎖,並不是加到記錄上的,而是加在兩個記錄之間的位置。GAP 鎖就是 RR 隔離級別相對於 RC 隔離級別,不會出現幻讀的關鍵。GAP鎖保證兩次當前讀之前,其他的事務不會插入新的滿足條件的記錄並提交。

所謂幻讀,就是同一個事務,連續做兩次當前讀 (例如:select * from t1 where id = 10 for update;),那麼這兩次當前讀返回的是完全相同的記錄 (記錄數量一致,記錄本身也一致),第二次的當前讀,不會比第一次返回更多的記錄 (幻象)。

如圖中所示:考慮到B+樹索引的有序性,有哪些位置可以插入新的滿足條件的項 (id = 10):

  • [6,c] 之前,不會插入id=10的記錄
  • [6,c] 與 [10,b] 間,可以插入 [10, aa]
  • [10,b] 與 [10,d] 間,可以插入[10,bb],[10,c]
  • [10,d] 與 [11, f] 間,可以插入[10,e],[10,z]
  • [11,f] 之後,不會插入id=10的記錄

因此,不僅將滿足條件的記錄鎖上 (X鎖),同時還通過GAP鎖,將可能插入滿足條件記錄的3個GAP給鎖上,保證後續的Insert不能插入新的id=10的記錄,也就杜絕了同一事務的第二次當前讀,出現幻象的情況。

當id是唯一索引時,則不需要加GAP鎖。因為唯一索引能夠保證唯一性,對於where id = 10 的查詢,最多只能返回一條記錄,而且新的 id= 10 的記錄,一定不會插入進來。

3.4. id無索引

當id無索引時,只能進行全表掃描,加鎖步驟:

  1. 聚簇索引上的所有記錄都加 X 鎖
  2. 聚簇索引每條記錄間的GAP都加上了GAP鎖。

如果表中有上千萬條記錄,這種情況是很恐怖的。這個情況下,MySQL也做了一些優化,就是所謂的semi-consistent read。semi-consistent read開啟的情況下,對於不滿足查詢條件的記錄,MySQL會提前放鎖。針對上面的這個用例,就是除了記錄[d,10],[g,10]之外,所有的記錄鎖都會被釋放,同時不加GAP鎖

4. 死鎖分析與案例

死鎖避免的一些辦法:

  1. 如果不同程式會併發存取多個表,儘量約定以相同的順序訪問表,可以大大降低死鎖機會。
  2. 在同一個事務中,儘可能做到一次鎖定所需要的所有資源,減少死鎖產生概率;

5.參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android studio安裝與配置 1、首先下載Android studio安裝包,可以從http://www.android-studio.org/下載最新版本,這裡採用3.0版本進行演示,對應安裝包為android-studio-ide-171.4408382-windows.exe,安裝包 ...
  • 我的有一篇博客上講瞭如何基於CXF搭建webservice,service層的介面會被部署到tomcat上,這一篇我就講一下如何在安卓中調用這些介面傳遞參數。 1.在lib中放入ksoap2的jar包並導入 2.在xml 配置文件中加入: 3.接下來就要通過代碼調用藉口了 4.若webservice ...
  • 如果鍵盤彈出覆蓋了原有的試圖,這種效果並不好,所以我們就要在鍵盤彈出的時候,監聽鍵盤的位置來改變我們一些試圖的位置,例如tableView列表等;在這裡推薦一個大牛ibireme寫的YYKeyboardManager,Git地址:https://github.com/ibireme/YYKeyboa ...
  • 1.NSPredicate驗證(謂詞匹配) 2.rangeOfString:option:直接查找 3.使用正則表達式類 4.常用正則表達式 一、校驗數字的表達式 1 數字:^[0-9]*$ 2 n位的數字:^\d{n}$ 3 至少n位的數字:^\d{n,}$ 4 m-n位的數字:^\d{m,n}$ ...
  • 這兩天主要就去做百度地圖去了,剛開始一頭霧水,什麼遮蓋物什麼興趣點..... 所以這篇文章先來介紹些基礎知識吧,遮蓋物,就是遮蓋在地圖上的東西,比如說 興趣點,就是圓圈圈的地點,包括什麼餐飲大廈之類之類的 好,然後說說地圖,首先你一定要去搞個KEY,因為沒有這個東西,你地圖都載入不出來...我就偷懶 ...
  • 打開資料庫(sqlite) 事務 多線程事務 ...
  • 簡述 簡單回顧並總結下不同的表連接語句有什麼異同之處以及一些概念。 建庫語句如下 由上代碼可知,分別創建了部門表和雇員表,雇員表和部門表是多對一的關係,技術部沒有雇員。 內連接 語法:inner join ... on ... inner可以省略。 概念:內連接查詢的結果是從兩個或兩個以上的表的組合 ...
  • 手工熱備(開庫狀態) 備份控制文件: alter database backup controlfile to '/u01/oradata/prod/con.bak1'; 備份數據文件(這裡用到pl/sql進行批處理,將查詢結果粘貼運行即可) beginfor i in (select tables ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...