表鎖相關結構: html
table->locks:數據字典table保存這個表上的全部表鎖信息 mysql
trx->lock.table_locks:每一個事務trx保存該事務所加的全部表鎖信息 算法
trx->lock.trx_locks:每一個事務trx保存該事務全部的鎖信息(包括行鎖)sql
表鎖類型:spa
IS IX S X AIhtm
AI:Auto-Increment Lock 也是表級的,語句結束後釋放而不事事務結束後釋放遞歸
http://dev.mysql.com/doc/refman/5.5/en/innodb-auto-increment-handling.html隊列
lock_table 加鎖流程事務
1 先從 trx->lock.table_locks 查找該事務是否已加對該表加鎖,若是所加的鎖比要加的鎖strong,則返回成功。其它狀況進入2rem
2 從 table->locks中查找該表是否被其餘事務加鎖。若是沒有加鎖進入3,若是已加鎖進入4
3 新建表鎖,返回加鎖成功
4 此時需判斷欲加的鎖是否與已加的鎖兼容。若是兼容進入5,不然進入6
5 新建表鎖,返回加鎖成功
6 新建表鎖,加入等待隊列,其中會穿插死鎖檢測流程。
新建表鎖會將新建的鎖放入table->locks,trx->lock.table_locks,trx->lock.trx_locks中。
附鎖兼容矩陣和鎖
/* LOCK COMPATIBILITY MATRIX
* IS IX S X AI
* IS + + + - +
* IX + + - - +
* S + - + - -
* X - - - - -
* AI + + - - -
鎖強度比較矩陣
/* STRONGER-OR-EQUAL RELATION (mode1=row, mode2=column)
* IS IX S X AI
* IS + - - - -
* IX + + - - -
* S + - + - -
* X + + + + +
* AI - - - - +
* See lock_mode_stronger_or_eq().
*/
記錄鎖
記錄鎖的存儲單位爲頁,一個事務在一個頁上的全部同類型鎖存儲爲一個lock,lock中經過bitmap來表示那些記錄已加鎖
行鎖類型:行鎖類型由基本類型和精確類型組成
行鎖基本類型:
LOCK_S,LOCK_X
行鎖精確類型:
LOCK_ORDINARY(NK):next-key lock 既鎖記錄也鎖記錄前的gap
LOCK_GAP(GAP):不鎖記錄,只鎖記錄前的gap
LOCK_REC_NOT_GAP(NG):只鎖記錄,不鎖記錄前的gap
LOCK_INSERT_INTENTION(I):insert時,若insert位置已有gap鎖,則需加LOCK_INSERT_INTENTION鎖
相關結構:
trx->lock.trx_locks:每一個事務trx保存該事務全部的鎖信息(表鎖和行鎖)
lock_sys->rec_hash:保存全部事務的行鎖信息
給記錄加S鎖以前必會給表加IS鎖
給記錄加X鎖以前必會給表加IX鎖
lock_rec_lock流程
分爲兩個階段快加鎖和慢加鎖階段,設記錄所在的頁爲p
1 快加鎖階段:
1.1 在 lock_sys->rec_hash中查找P的全部鎖,若是沒有鎖,則新建鎖返回成功;不然進入1.2
1.2 若是查找到只有一個鎖,且該所在的事務是當前事務,鎖類型也相同,則進入1.3,不然,進入第二階段快加鎖階段
1.3 如將該記錄已加鎖,則返回加鎖成功。不然進入1.4
1.4 將該記錄在lock的bitmap的位置爲已加鎖。返回成功。
2 慢加鎖階段
2.1 在lock_sys->rec_hash中遍歷查找當前事務對此記錄所加的鎖,若是已有鎖比欲加的鎖strong(見lock_rec_has_expl),則進入2.2,不然進入2.3。
2.2. 若是已有鎖狀態爲wait,則當前事務進入等待。不然直接返回加鎖成功。
2.3 在lock_sys->rec_hash中遍歷查找對此記錄所加的鎖 ,若是所在的事務是其餘事務且鎖不兼容則進入等待,不然進入2.4
2.4 如是隱式加鎖,則返回成功,不然進入2.5
2.5 若是當前記錄已有鎖等待(?),或當前事務沒有同類型的鎖設置bitmap位,則新建鎖返回成功(見lock_rec_add_to_queue) ,不然進入2.6
2.6 在當前事務同類型的鎖上設置bitmap位,返回成功
可見慢加鎖階段可能會兩次遍歷lock_sys->rec_hash的哈希桶。
行鎖的兼容和鎖比較沒有表鎖那麼簡單,是因爲引入行鎖精確類型致使的。
兼容性有如下原則
1 Gap type locks without LOCK_INSERT_INTENTION flag do not need to wait for anything. This is because different users can have conflicting lock types on gaps
2 Record lock (LOCK_ORDINARY or LOCK_REC_NOT_GAP does not need to wait for a gap type lock
3 Lock on gap does not need to wait for a LOCK_REC_NOT_GAP type lock
4 No lock request needs to wait for an insertintention lock to be removed.
能夠推出如下鎖兼容矩陣
GAP I NG NK
GAP + + + +
I +
NG + +
NK + +
對於鎖強度比較能夠參考lock_rec_has_expl,對於SUPREMUM記錄的處理比較特殊。
鎖強度矩陣(strong or equ)
GAP I NG NK
GAP - -
I -
NG - -
NK -
而對於SUPREMUM記錄NG和G是+。
關於 lock的bitmap,當記錄發生移動和page發生分裂和合並時都須要維護bitmap信息,參見lock_rec_move lock_update_merge_right
死鎖檢測
死鎖檢測發生在鎖等待時,參見lock_deadlock_check_and_resolve。表鎖和記錄鎖的檢測邏輯是同樣的。
採用等待圖法,事務是圖的節點,事務與該事務所等待的鎖所在的事務的連線構成線,若是發現圖中存在迴路,則表示系統中出現了死鎖。innodb採用圖的深度優先的非遞歸算法實現的。非遞歸採用棧來記錄節點的訪問軌跡。lock->trx->lock.deadlock_mark和 ctx->mark_start來判斷節點是否已訪問過。
死鎖檢測有三種結果:
1 檢查過程當中搜索節點過深(>200)或訪問的節點數過多(>1000000),或棧節點數超過srv_max_n_threads(?),則中止搜索,實際上就是認爲產生了死鎖。而不是採用超時的方法。此時將欲加的鎖的事務作爲犧牲者。
2 檢測到死鎖,這是須要選擇犧牲者。在當前事務和等待的鎖事務中選擇,原則是犧牲undo和持有鎖較少的事務。(trx_weight_ge)
做爲犧牲者的事務會回滾,從而釋放鎖持有的鎖,從而斷開了環路,解決了死鎖。
鎖的釋放
事務提交和回滾是會釋放鎖
另爲自增鎖在語句結束時釋放lock_unlock_table_autoinc