MySQL InnoDB 中的鎖機制

寫在前面

數據庫本質上是一種共享資源,所以在最大程度提供併發訪問性能的同時,仍須要確保每一個用戶能以一致的方式讀取和修改數據。鎖機制(Locking)就是解決這類問題的最好武器。mysql

首先新建表 test,其中 id 爲主鍵,name 爲輔助索引,address 爲惟一索引。程序員

CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` int(11) NOT NULL,
  `address` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idex_unique` (`address`),
  KEY `idx_index` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
複製代碼

INSERT 方法中的行鎖

可見,若是兩個事務前後對主鍵相同的行記錄執行 INSERT 操做,由於事務 A 先拿到了行鎖,事務 B 只能等待直到事務 A 提交後行鎖被釋放。同理,若是針對惟一索引字段 address 進行插入操做,也須要獲取行鎖,圖同主鍵插入過程相似,再也不重複。sql

可是,若是兩個事務都針對輔助索引字段 name 進行插入,不須要等待獲取鎖,由於輔助索引字段即便值相同,在數據庫中也是操做不一樣的記錄行,不會衝突。數據庫

Update 方法與 Insert 方法結果相似。bash

SELECT FOR UPDATE 下的表鎖與行鎖

事務 A SELECT FOR UPDATE 語句會拿到表 testTable Lock,此時事務 B 去執行插入操做會阻塞,直到事務 A 提交釋放表鎖後,事務 B 才能獲取對應的行鎖執行插入操做。併發

可是若是事務 A 的 SELECT FOR UPDATE 語句緊跟 WHERE id = 1 的話,那麼這條語句只會獲取行鎖,不會是表鎖,此時不阻塞事務 B 對於其餘主鍵的修改操做ide

輔助索引下的間隙鎖

先看下 test 表下的數據狀況:性能

mysql> select * from test;
+----+------+---------+
| id | name | address |
+----+------+---------+
|  3 |    1 |       3 |
|  6 |    1 |       2 |
|  7 |    2 |       4 |
|  8 |   10 |       5 |
+----+------+---------+
4 rows in set (0.00 sec)
複製代碼

間隙鎖能夠說是行鎖的一種,不一樣的是它鎖住的是一個範圍內的記錄,做用是避免幻讀,即區間數據條目的忽然增減。解決辦法主要是:ui

  • 防止間隙內有新數據被插入,所以叫間隙鎖
  • 防止已存在的數據,在更新操做後成爲間隙內的數據(例如更新 id = 7name 字段爲 1,那麼 name = 1 的條數就從 2 變爲 3

InnoDB 自動使用間隙鎖的條件爲:spa

  • Repeatable Read 隔離級別,這是 MySQL 的默認工做級別
  • 檢索條件必須有索引(沒有索引的話會走全表掃描,那樣會鎖定整張表全部的記錄)

InnoDB 掃描索引記錄的時候,會首先對選中的索引行記錄加上行鎖,再對索引記錄兩邊的間隙(向左掃描掃到第一個比給定參數小的值, 向右掃描掃描到第一個比給定參數大的值, 以此構建一個區間)加上間隙鎖。若是一個間隙被事務 A 加了鎖,事務 B 是不能在這個間隙插入記錄的。

咱們這裏所說的 「間隙鎖」 其實不是 GAP LOCK,而是 RECORD LOCK + GAP LOCK,InnoDB 中稱之爲 NEXT_KEY LOCK

下面看個例子,咱們建表時指定 name 列爲輔助索引,目前這列的取值有 [1,2,10]。間隙範圍有 (-∞, 1]、[1,1]、[1,2]、[2,10]、[10, +∞)

Round 1:

  • 事務 A SELECT ... WHERE name = 1 FOR UPDATE;
  • 對 (-∞, 2) 增長間隙鎖
  • 事務 B INSERT ... name = 1 阻塞
  • 事務 B INSERT ... name = -100 阻塞
  • 事務 B INSERT ... name = 2 成功
  • 事務 B INSERT ... name = 3 成功

Round 2:

  • 事務 A SELECT ... WHERE name = 2 FOR UPDATE;
  • 對 [1, 10) 增長間隙鎖
  • 事務 B INSERT ... name = 1 阻塞
  • 事務 B INSERT ... name = 9 阻塞
  • 事務 B INSERT ... name = 10 成功
  • 事務 B INSERT ... name = 0 成功

Round 3:

  • 事務 A SELECT ... WHERE name <= 2 FOR UPDATE;
  • 對 (-∞, +∞) 增長間隙鎖
  • 事務 B INSERT ... name = 3 阻塞
  • 事務 B INSERT ... name = 300 阻塞
  • 事務 B INSERT ... name = -300 阻塞

InnoDB 鎖機制總結

參考資料

寫在最後

這是一個不定時更新的、披着程序員外衣的文青小號,歡迎關注。

相關文章
相關標籤/搜索