鎖是數據庫系統區分與文件系統的一個關鍵特性。爲了保證數據一致性,必須有鎖的介入。數據庫系統使用鎖是爲了支持對共享資源進行併發訪問,提供數據的完整性和一致性。mysql
使用命令能夠查詢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
存儲引擎經過行多版本控制的方式來讀取當前執行時間數據庫中行的數據。若是讀取的行正在執行DELETE
或UPDATE
操做,這時讀取操做不會所以等待行上鎖的釋放。而是會去讀取行的一個快照數據。
快照數據是改行以前版本的數據,經過undo段來完成的,undo端是用來在事務中回滾數據,所以快照數據自己是沒有額外的開銷。
這是INNODB
存儲引擎的默認設置下的默認讀取方式,即讀取不會佔用和等待表上的鎖。可是在不一樣事務隔離界別下,讀取的方式不一樣。
快照數據其實就是當前行以前的歷史版本,每行記錄可能有多個版本,通常稱這種技術爲行多版本技術。由此帶來的併發控制,稱之爲多版本併發控制。
READ COMMINTED
和REPEATABLE 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語句後當即釋放。雖然必定程度上提高了併發效率,可是仍是有問題。
5.1.22版本以後,提供了一種輕量級互斥量額自增加實現機制,大大提升了自增加插入的性能。提供參數innodb_autoinc_lock_mode
來控制自增加的模式。
先對自增加的插入進行分類:
插入類型 | 說明 |
---|---|
insert-like |
全部的插入語句 |
simple inserts |
插入前就能肯定插入行數的語句。不包括INSERT ... ON DUPLICATE KEY UPDATE 這類SQL |
bulk inserts |
指在插入前不能肯定獲得插入行數的語句,如INSERT...SELECT ,LOAD 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 Lock
:Record Lock
+ Gap Lock
,鎖定一個範圍,而且鎖定記錄自己。
Next-Key Lock
是INNODB
對於行查詢的採用的鎖定算法。例如一個索引有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
算法,避免了不可重複讀的現象。
就是一個事務的更新操做會被另外一個事務的更新操做所覆蓋,從而致使數據的不一致。
在數據庫層面,任何事務隔離界別都會經過加鎖,來避免出現丟失更新的狀況。通常出如今用戶編程時發生。
解決方式,是將事務並行操做變成串行的操做。因此要對用戶讀取的記錄上加一個排他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邊的定義爲:
而後就能夠畫出wait-for graph
是一種較爲主動的死鎖檢測機制,在每一個事務請求鎖併發生等待時會判斷是否存在賄賂,若存在死鎖,選擇回滾undo量最小的事務。
事務發生死鎖的機率與如下幾點因素有關:
是指將當前鎖的顆粒度下降。舉例說,數據庫能夠把一個表的1000行鎖升級爲一個頁鎖,或者將頁鎖升級爲表鎖。
INNODB不存在所省級的問題,由於其不是根據每一個記錄來產生行鎖的,想法,根據每一個事務訪問的每一個頁對鎖進行管理的,採用的是位圖的方式。所以無論一個事務鎖住頁中一個記錄仍是多個記錄,其開銷一般是一致的。