InnoDB 存儲引擎的鎖學習

本文主要是針對《Mysql技術內幕:InnoDB 存儲引擎》一書中第六章關於 InnoDB 存儲引擎中鎖的學習總結。mysql

鎖是數據庫系統區別於文件系統的一個關鍵特性,爲了支持對共享資源的併發訪問,提供數據的完整性和一致性,咱們必須爲數據加鎖,InnoDB 提供了一致性非鎖定讀、行級鎖的支持。算法

共享鎖(S Lock)和排他鎖(X Lock)

  • 共享鎖就是多個事務對於同一數據能夠共享一把鎖,都能訪問到數據,可是隻能讀不能修改
  • 排他鎖是容許當前事務修改和刪除數據,當一個事務獲取了一個數據行的排他鎖,其餘事務就不能再獲取該行的其餘鎖
  • InnoDB 支持多粒度鎖,既支持在表粒度上 S 鎖和 X 鎖,也支持在行粒度上加 S 鎖 和 X 鎖
  • 鎖的兼容性是指是否能夠同時給某個數據加兩個鎖,S 鎖和 X 鎖的兼容性以下:
鎖類型 S 鎖 X 鎖
S 鎖 兼容 不兼容
X 鎖 兼容 不兼容

意向鎖

  • 意向鎖是一種表級鎖
  • 用戶要獲取某個表的行鎖,必須先獲取該表的意向鎖
  • 意向鎖是 InnoDB 本身維護的,用戶沒法手動操做意向鎖
  • 意向鎖分爲意向共享鎖(IS)和意向排他鎖(IX)
  • 意向鎖和意向鎖之間相互兼容,意向鎖和行級鎖也相互兼容
  • 意向鎖和表鎖的兼容性以下:
鎖類型 IS 鎖 IX 鎖
表 S 鎖 兼容 不兼容
表 X 鎖 兼容 不兼容
  • 若是事務想對某個表加鎖,那麼先會檢查是否與該表的意向鎖相兼容,若是不兼容則不能加表級鎖,而不須要檢查是否與該表中行級鎖的兼容性
  • 意向鎖在保證併發性的前提下,實現了行鎖和表鎖共存且知足事務隔離性的要求

一致性非鎖定讀

  • 一致性非鎖定讀是基於多版本併發控制(MVCC)技術實現的
  • 多版本併發控制(MVCC) 技術是指一個行記錄可能有不止一份快照數據,而 MVCC 是經過事務的 undo 日誌實現的
  • 一致性非鎖定讀是 InnoDB 默認的讀取方式,即讀取數據時不佔用和等待表上的鎖
  • 一致性非鎖定讀只有在隔離級別爲 REPEATABLE READ 和 READ COMMITTED 下才會支持
  • 當事務的隔離級別爲 REPEATABLE READ 時,同一個事務中的一致性讀都是讀取的是該事務下第一次查詢所創建的快照
  • 當事務隔離級別爲 READ COMMITTED 時,同一事務下每次查詢都是讀的最新一份數據快照,因此可能會違背數據庫的隔離性,由於同一份事務裏可能讀取到兩份不一樣的數據
  • 一致性非鎖定讀能夠看到該時間點以前提交的事務所作的更改而且不會被以後的修改或者未提交事務所影響

一致性鎖定讀

在數據庫的默認隔離級別爲 REPEATABLE READ 下,Select 查詢默認爲一致性非鎖定讀,若是想要使用一致性鎖定讀,須要顯示給 Select 查詢加鎖sql

Select ... FOR UPDATE          - 給讀取的行加上 X 鎖
Select ... LOCK IN SHARE MODE  - 給讀取的行加上 S 鎖
複製代碼

自增加和鎖

  • 由於主鍵索引是有序的,咱們在主鍵上設置自增屬性,能夠有效的減小索引頁的分裂和數據的移動。
  • 使用關鍵字 AUTO_INCREMENT 給主鍵加自增加特性:
CREATE TABLE `test` (
  `id` int(1) NOT NULL AUTO_INCREMENT,
  `name` varchar(8) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12;
複製代碼
  • 對於自增加列的索引比較特殊,是用 AUTO-INC Locking 方式先的,是一種表鎖機制
  • 自增加的列必須是索引,同時必須是索引的第一個列
  • 爲了提升插入性能,鎖並非在一個事務完成時才釋放,而是在完成對自增加值的插入 SQL 後當即釋放
  • 對於併發插入存在必定性能問題,必須等待前一個自增加插入完成才能繼續插入,雖然不是等待整個事務結束
  • 從 MySQL5.1.22 版本開始,InnoDB 存儲引擎引擎中提供了一種輕量級互斥量的自增加實現機制,這種機制大大提升了自增加值插入的性能,能夠經過參數 innodb_autoinc_lock_mode 對自增加索引模式進行設置:
- 0:這是 MySQL5.1.22 版本以前自增加的實現方式,即經過表鎖的 AUTO-INC Locking 方式 
- 1:這是該參數的默認值。對於 simple inserts 該值會用互斥量去對內存中的計數器進行累加的操做,
     對於 bulk inserts 仍是使用傳統表鎖的 AUTO-INC Locking方式
- 2:在這個模式下,對於全部的 insert-like 自增加的產生都是經過互斥量,
     而不是經過 AUTO-INC Locking 的方式,顯然這時性能最高的方式。
     然而會帶來必定的問題。由於併發插入的存在,在每次插入時,自增加的值可能不是連續的。
複製代碼

外鍵和鎖

  • 在InnoDB存儲引擎中,對於一個外鍵列,若是沒有顯示地對這個列加索引,InnoDB存儲引擎會自動對其加一個索引,由於這樣能夠避免表鎖
  • 對於外鍵值的插入或更新,首先須要檢查父表中的記錄,即 SELECT 父表
  • 在外鍵檢查對於父表的 SELECT 操做時,不是使用一致性非鎖定讀的方式,由於這會發生數據不一致的問題,InnoDB 會主動對父表加一個 S 鎖

行鎖算法

  • InnoDB 三種行鎖算法:
- Record Lock(行鎖):單個行記錄上的鎖,鎖定單條索引記錄。
- Gap Lock(間隙鎖):鎖定一個範圍,但不包括記錄自己
- Next-Key Lock(臨鍵鎖):是行鎖和間隙鎖的結合,鎖定一個範圍,而且鎖定記錄自己。
複製代碼
  • 行鎖在 InnoDB 中是基於索引實現的,因此一旦某個加鎖操做沒有使用索引,那麼該鎖就會退化爲表鎖
  • 對於行的等值查詢,通常狀況下都是用 Next-Key Lock,若是發現查詢條件沒有設置索引,則退化爲表鎖,若是發現查詢條件是惟一索引,則升級爲行鎖
  • 在根據非惟一索引進行區間查詢時,會使用間隙鎖,使用間隙鎖鎖住的是一個區間,而不只僅是這個區間中的每一條數據:
- 此時全部在(1,10)區間內的記錄行都會被鎖住
SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;
複製代碼
  • 每一個數據行上的非惟一索引列上都會存在一把臨鍵鎖,當某個事務持有該數據行的臨鍵鎖時,會鎖住一段左開右閉區間的數據
  • 假設數據庫表中某個非惟一索引存在如下值 10,24,32,45,該表中 age 列潛在的臨鍵鎖有:(-∞, 10], (10, 24], (24, 32], (32, 45],(45, +∞]
  • 在根據非惟一索引對記錄行進行 UPDATE \ FOR UPDATE \ LOCK IN SHARE MODE 操做時,InnoDB 會獲取該記錄行的臨鍵鎖 ,並同時獲取該記錄行下一個區間的間隙鎖
- 執行如下 SQL 時,InnoDB 會獲取 24 對應的臨鍵鎖 (10, 24],並獲取下一個區間的間隙鎖 (24, 32),因此最終在 (10, 32) 區間加上間隙鎖
SELECT * FROM table WHERE age = 24 FOR UPDATE;

- 以後若是在其它事務中執行如下命令,則該命令會被阻塞:
INSERT INTO table(age, name) VALUES(26, 'Ezreal');

複製代碼
  • Gap Lock 是爲了阻止事務將數據插入同一範圍內,從而解決幻讀現象,也就是兩次讀不一致狀況
  • 用戶能夠經過如下兩種方式關閉間隙鎖,這種狀況下除了外鍵約束和惟一性檢查之外,其他狀況都是用記錄鎖:
方式一:將事務的隔離級別設置成 READ COMMITTED
方式二:將參數 innodb_locks_unsafe_for_binlog 設置爲 1
複製代碼

鎖問題

  • 髒讀:
- 髒讀是指在併發狀況下,當前事務能夠讀到其它事務未提交到數據
- 髒讀違反了數據庫事務的隔離性要求
- InnoDB 中髒讀的發生條件是事務的隔離級別爲 READ UNCOMMITED
複製代碼
  • 不可重複讀:
- 不可重複讀是指同一個事務內,屢次讀取同一個數據集合結果不一致現象
- 不可重複讀違反了數據庫事務的一致性要求
- InnoDB 經過 next-key 算法來避免不可重複讀現象
- 爲了不不可重複讀現象發生,須要將事務的隔離級別設置爲 REPEATABLE READ 以上
複製代碼
  • 丟失更新:
- 丟失更新是指一個事務的更新操做會被令一個事務的更新操做覆蓋
- 在當前數據庫任何隔離級別下,都不會致使數據庫丟失更新現象發生
複製代碼

阻塞和死鎖

  • 阻塞是指一個事務中的鎖須要等待另一個事務的鎖釋放資源才能繼續執行
- 阻塞主要是爲了確保事務的併發可以正常有序的執行
- 經過參數 innodb_lock_wait_timeout 來設置事務的超時等待時間,默認是 50 s
- 經過參數 innodb_rollback_on_timeout 來設置事務超時是否進行回滾,默認不進行回滾
- 超時狀況下默認不進行回滾,須要業務來捕獲超時異常主動進行回滾,不然會致使數據的不一致性發生

複製代碼
  • 死鎖是指兩個及兩個以上的事務爲了爭奪資源而形成的一種相互等待的現象
innoDB 解決死鎖的兩種方案:
- 事務超時回滾事務,這種方案的缺點是若是超時的事務佔用的比重比較大,回滾很是耗時
- 採用 wait-for graphic(等待圖) 來進行死鎖檢查,經過深度優先遍歷算法主動檢查死鎖發生,釋放回滾 undo 量最小的事務
複製代碼
相關文章
相關標籤/搜索