MySQL技術內幕讀書筆記(七)——鎖

​ 鎖是數據庫系統區分與文件系統的一個關鍵特性。爲了保證數據一致性,必須有鎖的介入。數據庫系統使用鎖是爲了支持對共享資源進行併發訪問,提供數據的完整性和一致性。mysql

lock與latch

​ 使用命令能夠查詢latch信息算法

SHOW ENGINE INNODB MUTEX;

​ 對於lock信息查看就很直觀sql

一、SHOW ENGINE INNODB STATUS

二、information_schema架構下的表INNODB_TRX  INNODB_LOCKS  INNODB_LOCK_WAITS

INNODB存儲引擎中的鎖

INNODB存儲引擎實現了以下兩種標準的行級鎖:數據庫

  • 共享鎖S Lock:容許事務讀一行數據
  • 排他鎖X Lock:容許事務刪除或更新一行數據

​ 某事務獲取了行R的X鎖,那麼另外的事務不能當即獲取行R的X和S鎖稱之爲鎖不兼容。編程

​ 某事物獲取了行R的S鎖,那麼另外的事務不能當即獲取行R的X鎖,但能獲取行R的S鎖,稱爲兼容。架構

​ 此外,INNODB存儲引擎支持在不一樣顆粒度上進行加鎖操做,INNODB存儲引擎支持一種額外的鎖方式,稱之爲意向鎖。意向鎖是將鎖定的對象分爲多個層次,意向鎖意味着事務但願在更細顆粒上進行枷鎖。
併發

​ 若將上鎖的對象當作一棵樹,那麼對最下層的對象上鎖,也就是對最細顆粒度的對象進行上鎖。例如對行記錄R上X鎖,就要對數據庫A、表一、頁上加意向鎖IX,最後對記錄R上X鎖。若對R加X鎖以前,已經有事務對錶1進行了S表鎖,那麼就須要等待S鎖釋放後再能在表1上加意向鎖IX。性能

​ 意向鎖即爲表級別的鎖,設計目的主要是爲了在一個事務中揭示下一行將被請求的鎖類型。大數據

  • 意向共享鎖IS Lock:事務想要獲取一張表中某幾行的共享鎖
  • 意向排他鎖IX Lock:事務想得到一張表中某幾行的排他鎖

​ 經過下面查詢當前鎖請求:優化

SHOW ENGINE INNODB STATUS\G;

​ 還有三張在information_schema下的表能夠查詢鎖狀況

# 只顯示當前運行的INNODB事務,並不能判斷鎖的一些狀況
SELECT * FROM information_schema.INNODB_TRX\G;

# 能夠清晰的看到當前鎖信息,可是lock_data並不是是可信值
# 事務狀況小的時候能夠看這個表,可是事務量大的狀況下比較難判斷
select * from information_schema.INNODB_LOCKS\G;

# 很直觀的反映當前事務的等待,知道誰堵塞了誰。
SELECT * FROM infomation_schema.INNODB_LOCK_WAITS\G;

​ 最後提供一個經常使用的聯合查詢,進行鎖信息查詢。

SELECT r.trx_id waiting_trx_id,
       r.trx_mysql_thread_id waiting_thread,
       r.trx_query waiting_query,
       b.trx_id blocking_trx_id,
       b.trx_mysql_thread_id blocking_thread,
       b.trx_query blocking_query
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx b
ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.innodb_trx r
ON r.trx_id = w.requsting_trx_id\G;

一致性非讀定鎖

​ 指INNODB存儲引擎經過行多版本控制的方式來讀取當前執行時間數據庫中行的數據。若是讀取的行正在執行DELETEUPDATE操做,這時讀取操做不會所以等待行上鎖的釋放。而是會去讀取行的一個快照數據。

​ 快照數據是改行以前版本的數據,經過undo段來完成的,undo端是用來在事務中回滾數據,所以快照數據自己是沒有額外的開銷。

​ 這是INNODB存儲引擎的默認設置下的默認讀取方式,即讀取不會佔用和等待表上的鎖。可是在不一樣事務隔離界別下,讀取的方式不一樣。

​ 快照數據其實就是當前行以前的歷史版本,每行記錄可能有多個版本,通常稱這種技術爲行多版本技術。由此帶來的併發控制,稱之爲多版本併發控制。

READ COMMINTEDREPEATABLE READ(默認事務隔離級別)的事務隔離級別下,INNODB存儲引擎使用非鎖定的一致性讀。

  • READ COMMITED:老是讀取被鎖定行的最新一份快照數據,理論上來說是違反了ACID中的I特性
  • REPEATABLE READ:老是讀取事務開始時的行數據版本。

一致性鎖定讀

​ 在事務隔離級別REPEATABLE READ模式下,SELECT使用一致性非鎖定讀。可是某些狀況下,須要顯示地對數據庫讀取操做進行加鎖以保證數據邏輯的一致性。這要求數據庫支持加鎖語句。

# 對讀取的行記錄加一個X鎖,其餘事務不能對已鎖定的行加任何鎖。
SELECT ... FOR UPDATE

# 對讀取的記錄加上一個S鎖,其餘事務能夠向被鎖定的行加S鎖,可是若是加X鎖,會被堵塞。
SELECT ... LOCK IN SHARE MODE

​ 此外,這種SELECT語句必須在一個事務中進行執行。務必加上

BEGIN, START TRANSACTION
或者
SET AUTOCOMMIT = 0

自增加與鎖

​ 在INNODB存儲引擎的內存結構中,對每一個含有自增加值的表都有一個自增加計數器。當對含有自增加的計數器的表進行插入操做時,這個計數器會被初始化,執行以下的語句來獲得計數器的值:

SELECT MAX(auto_inc_col) FROM t FOR UPDATE;

​ 5.1.22版本以前,自增加實現機制是:AUTO-INC Locking

​ 是一種特殊的表鎖機制,爲了提升插入的性能,鎖不是在一個事務完成後才釋放,而是在完成對自增加值插入的SQL語句後當即釋放。雖然必定程度上提高了併發效率,可是仍是有問題。

  • 事務依舊仍是要等待前一個插入的完成(雖然不用等待事務的完成)。
  • 對於INSERT...SELECT的大數據量的插入會影響插入的性能,由於另外一個事務的插入會被堵塞。

​ 5.1.22版本以後,提供了一種輕量級互斥量額自增加實現機制,大大提升了自增加插入的性能。提供參數innodb_autoinc_lock_mode來控制自增加的模式。

​ 先對自增加的插入進行分類:

插入類型 說明
insert-like 全部的插入語句
simple inserts 插入前就能肯定插入行數的語句。不包括INSERT ... ON DUPLICATE KEY UPDATE這類SQL
bulk inserts 指在插入前不能肯定獲得插入行數的語句,如INSERT...SELECTLOAD DATA
mixed-mode inserts 指插入中有一部分的值是自增加的,有一部分是肯定的。如

​ 分析參數innodb_autoinc_lock_mode以及各個設置下自增的影響,總共有三個有效值能夠設定,即0、一、2。

innodb_autoinc_lock_mode 說明
0 5.1.22版本以前自增加的實現方式
1 參數默認值。對於simple inserts,會用互斥量去對內存中的計數器進行累加操做。對於bulk inserts使用傳統的AUTO-INC Locking方式。這種配置下,若是不考慮回滾操做的話,自增列鍵值增加仍是連續的。而且在這種方式下,statement-based方式的replication仍是能很好的工做。
2 對於全部INSERT-like自增加值的產生都是經過互斥量而不是AUTO-INC Locking方式。顯然,這是性能最高的方式,可是由於併發插入的存在,在每次插入時,自增加的值可能不是連續的。最終昂要的是,基於Statement-Base Replication會出現問題。所以,使用這個模式,任什麼時候候都應該使用row-base replication。這樣才能保證最大的併發性能以及replication主從數據的一致性

外鍵和鎖

​ 對於外鍵值的插入或更新,首先要查詢父表中的記錄,即SELECT父表,可是對於父表的SELECT操做,不是使用一致性非鎖定讀的方式,由於這樣會發生數據不一致的問題,所以這時使用的是SELECT ... LOCK IN SHARE MODE 方式,即主動對父表加上一個S鎖。若是這時父表已經加上了X鎖,那麼子表上的操做會被堵塞

鎖的算法

行鎖的三種算法

  • Record Lock:單個行記錄上的鎖

    老是會去鎖住索引記錄,若是沒有顯示設置索引,會使用隱式的主鍵來進行鎖定。

  • Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄自己

  • Next-Key LockRecord Lock + Gap Lock,鎖定一個範圍,而且鎖定記錄自己。

Next-Key LockINNODB對於行查詢的採用的鎖定算法。例如一個索引有10,11,13和20這四個值,那麼索引可能被Next-Key Locking的區間爲:

​ 當插入新紀錄12時,鎖定的範圍會變成

​ 然而,當查詢的索引含有惟一屬性時,INNODB存儲引擎會對Next-Key Lock進行優化,將其降級爲Record Lock,即僅鎖住索引自己。

​ 對於輔助索引,不只加上的是Next-Key Lock,還會對輔助索引下一個鍵值加上gap lock

解決Phantom Problem

Phantom Problem(幻象問題):是指在同一事務下,連續執行兩次一樣的SQL語句可能致使不一樣的結果,第二次的SQL語句可能會返回以前不存在的行。

​ 在默認事務隔離級別REPEATABLE READ下,INNODB存儲引擎採用Next-Key Locking來避免。

​ 例如:

CREATE TABLE t (a INT PRIMARY KEY)
BEGIN
SELECT * FROM T
WHRER a > 2 FOR UPDATE;

​ 這個事務中。對於a>2的範圍加上了X鎖,所以任何對和這個返回的插入都是不被容許的,因此避免的幻讀。

鎖問題

髒讀

​ 髒數據是指未提交的數據。

​ 髒讀指的是在不一樣的事務下,當前事務能夠讀到另外事務未提交的數據,簡單來講就是能夠讀到髒數據。

​ 在事務隔離級別READ UNCOMMITTED下才會發生,可是實際生產並不會使用這個級別,可是能夠將replication環境中的slave節點設置爲這個級別,以提高性能。

不可重複讀

​ 不可重複讀是指在一個事務內屢次讀取同一數據集合。在這個事務尚未結束時,另一個事務也訪問了該同一數據集合,並作了一些DML操做。所以,在第一個事務中的兩次讀數據之間,因爲第二個事務的修改,那麼第一個事務兩次讀到的數據多是不同的。這樣就發生了在一個事務內兩次讀到的數據是不同的狀況,這種狀況稱爲不可重複讀。

​ 在MYSQL官方文檔中,不可重複度被定義爲Phantom Problem,可是在事務級別READ REPEATABLE級別下,採用Next-Key Lock算法,避免了不可重複讀的現象。

丟失更新

​ 就是一個事務的更新操做會被另外一個事務的更新操做所覆蓋,從而致使數據的不一致。

​ 在數據庫層面,任何事務隔離界別都會經過加鎖,來避免出現丟失更新的狀況。通常出如今用戶編程時發生。

  • 事務T1查詢一行的數據,放入本地內存,並顯示給一個終端用戶USER1
  • 事務T2也查詢一行的數據,放入本地內存,並顯示給一個終端用戶USER2
  • USER1修改這行數據,更新數據庫並提交
  • USER2修改這行數據,更新數據庫並提交

​ 解決方式,是將事務並行操做變成串行的操做。因此要對用戶讀取的記錄上加一個排他X鎖便可。

select cash into @cash
form account
where user = pUser FOR UPDATE;

堵塞

​ 由於不一樣鎖之間的兼容性關係,在有些時刻一個事務中的鎖須要等待另一個事務中的鎖釋放它所佔用的資源,這就是堵塞。

​ 參數innodb_lock_wait_timeout用來控制等待的時間,默認50秒,是能夠動態設置的,

​ 參數innodb_rollback_on_timeout用來設定是否在等待超時時對進行中的事務進行回滾操做。默認是OFF

死鎖

死鎖的概念

​ 死鎖是指兩個或兩個以上的事務在執行過程當中,因爭奪鎖資源而形成的一種互相等待現象。

​ 解決死鎖問題最簡單方式是不要有等待,將任何的等待都轉化爲回滾,而且事務從新開始。毫無疑問,這的確能夠避免死鎖問題的產生。可是性能不好。

​ 解決死鎖問題最簡單方法是超時,即當兩個事務互相等待時,當一個等待時間超過閾值時,其中一個事務進行回滾,另一個事務就能繼續進行。雖然簡單,可是僅僅根據FIFO進行回滾,若超時的事務佔據權重比較大,會浪費較多時間。

​ 因此除了超時機制外,還廣泛採用wait-for graph等待圖的方式來進行死鎖檢測。這要求數據庫保存如下兩種信息:

  • 鎖的信息鏈表
  • 事務等待鏈表

​ 經過上述鏈表能夠構造出一張圖,若是存在賄賂,那就表明存在死鎖,所以資源間互相發生等待。在圖中,事務T1執向T2邊的定義爲:

  • 事務T1等待事務T2所佔用的資源
  • 事務T1最終等待T2所佔用的資源,也就是事務之間在等待相同的資源,而事務T1發生在事務T2的後面。

​ 而後就能夠畫出wait-for graph

​ 是一種較爲主動的死鎖檢測機制,在每一個事務請求鎖併發生等待時會判斷是否存在賄賂,若存在死鎖,選擇回滾undo量最小的事務。

死鎖機率

​ 事務發生死鎖的機率與如下幾點因素有關:

  • 系統中事務的數量n,數量越多發生死鎖的機率越大。
  • 每一個事務操做的數量r,每一個事務操做的數量越多,發生死鎖的機率越大
  • 操做數據的集合R,越小則發生死鎖的機率越大。

鎖升級

​ 是指將當前鎖的顆粒度下降。舉例說,數據庫能夠把一個表的1000行鎖升級爲一個頁鎖,或者將頁鎖升級爲表鎖。

​ INNODB不存在所省級的問題,由於其不是根據每一個記錄來產生行鎖的,想法,根據每一個事務訪問的每一個頁對鎖進行管理的,採用的是位圖的方式。所以無論一個事務鎖住頁中一個記錄仍是多個記錄,其開銷一般是一致的。

相關文章
相關標籤/搜索