InnoDB之鎖機制

2022-06-08 12:51:08 字數 4293 閱讀 1695

前兩天聽了姜老大關於innodb中鎖的相關培訓,剛好也在看這方面的知識,就順便利用時間把這部分知識做個整理,方便自己理解。主要分為下面幾個部分

1. innodb同步機制

innodb儲存引擎有兩種同步機制選擇,一種是mutex,其是完全的互斥方法。另一種是rw-lock,可以給臨界資源加上s-latch或者x-latch。其中s-latch允許併發的讀取操作,而x-latch是完全的互斥操作。

mutex是基於test-and-set機制實現的,在其基礎上做了優化。具體的流程為:

(1)執行緒呼叫test-and-set返回1,說明其他執行緒已經持有了這把鎖,此時先進行自旋。自旋時間大約為20us

(2)再次獲取mutex,如果還是不能獲取到就放入wait array中,等待被喚醒。

2. lock和latch的區別

lock

latch

鎖定物件

事務

執行緒鎖定持續時間

整個事務過程

臨界資源持有過程

模式行鎖、表鎖、意向鎖

讀寫鎖、互斥量

死鎖智慧型死鎖檢測

無死鎖檢測與處理機制

最重要的區別:lock鎖定時間從事物開始一直持續到事務結束,commit之後才會釋放鎖;而latch就是通常意義上的鎖,鎖定臨界資源,等其使用完之後就會釋放鎖。

3. 行鎖/表鎖/意向鎖

(1)行鎖和表鎖比較簡單,主要理解共享鎖、排他鎖以及兩者的相容關係。

(2)意向鎖是實現多粒度鎖的一種方式。innodb和myisam不一樣,可以同時支援行鎖和表鎖,對行鎖的支援極大的提高了資料庫的效能。那什麼時候會用到表鎖呢?

這些情況下innodb都會上表鎖。

那問題就來了,如果事務a正在修改user表的某條記錄,事務b正好執行

select * from user where name = "libis" for update;事務b會得到執行嗎?讀者可以驗證一下,事務b會被夯住,下圖是事務b被夯住的情況,其中trx_id(

14376

)被14378阻塞:

為什麼會這樣?那可以這樣想,如果事務b不會被事務a阻塞,會發生什麼?假設事務b沒有被事務a阻塞,事務b先執行了一次 

select * from user where name = "libis" for update得到了一行記錄,此時事務a正好了修改了這條記錄,然後提交了,事務b再次執行上述select語句就肯定會得到不同的記錄,這就違背了事務隔離性的要求。意向鎖就是為了解決這樣的問題。

事務a修改user表的記錄r,會給記錄r上一把x行鎖,同時會給user表上一把意向排他鎖(ix),這時事務b要給user表上表級排他鎖就會被阻塞。意向鎖通過這種方式實現了行鎖和表鎖共存且滿足事務隔離性的要求。

4. 對可重複讀和幻讀的理解

(1)什麼是不可重複讀?什麼是幻讀?兩者的區別是什麼

不可重複讀重點在同乙個事務多次讀同一條記錄的時候,出現讀到的資料不一致的情況。innodb通過mvcc的方式避免了不可重複讀,即一致性的非鎖定讀。

幻讀重點在同乙個事務多次執行相同的sql,可能返回之前不存在的行,或者之前存在的行之後不存在了。innodb使用next-key lock演算法避免了幻讀,即一致性的鎖定讀。

預設情況下,innodb使用一致性的非鎖定讀,即讀取不會被阻塞。然而有些情況下使用者希望通過鎖定讀取的方式保證資料的一致性,這時可以通過語法lock in share mode或for update主動對讀取進行加鎖操作,稱這種方式為一致性的鎖定讀。

(2)innodb如何避免不可重複讀?

見另一篇博文:innodb之mvcc機制與不可重複讀

(3)innodb如何避免幻讀?

在了解具體實現之前,首先對innodb中鎖的演算法有一定了解,innodb提供了三種鎖演算法:

innodb就是使用next-key lock演算法避免幻讀的,具體的實現方式可以舉例如下:

有一張user表,只有一列uid,見下圖:

裡面有三條記錄:

開啟事務a,執行select * from user where uid > 4 for update,沒有commit :

開啟另乙個事務b,執行insert into user values (5)

會發現這個事務會被夯住,執行下面查詢資料庫鎖的語句可以看到,事務b被事務a阻塞住了,即事務b在等事務持有的鎖:

再通過執行show engine innodb status檢視具體的鎖資訊,可以看到事務a上了一把鎖鎖住了某個gap導致事務b等待: 

這會大家就應該知道next-key lock的意義了吧,它鎖住的是乙個範圍,而不是某一條記錄,就拿上例來說,事務a鎖住的是[4,6),[6,無窮大)這兩個範圍,因此向user中插入5是不可行的,直至事務a結束事務b才能執行成功,這樣就可以避免幻讀。

5. innodb中鎖的實現機制

(1)頁鎖物件 + 位圖 的實現方式

innodb中鎖是根據頁的組織形式進行管理的,行鎖在innodb中的定義如下:

struct lock_rec_struct

其中space/page_no可以唯一決定乙個頁,nbits是乙個位圖。因此要檢視某行記錄是否上鎖,只需要根據space/page_no找到對應的頁,然後根據點陣圖中對應位置是否是1來決定此行記錄是否上鎖。

給某條記錄上鎖,首先檢視記錄所在頁是否已經有鎖物件,如果鎖物件已經存在,則將點陣圖上對應位置置1。如果不存在,則生成乙個鎖物件,然後將位圖對應位置置1;

這種鎖的實現機制可以最大程度地重用鎖物件,節省系統資源,不存在鎖公升級的問題。可想而知,如果每個行鎖都生成乙個鎖物件,將會導致嚴重的效能損耗,比如接近於全表掃瞄的查詢就會生成大量的鎖物件,記憶體開銷將會很大。點陣圖的方式很好地避免了這個問題。

(2)通過事務或(space,page_no)再hash的方式組織頁鎖物件

innodb提供了兩種方式對行鎖進行訪問:

通過事務中的trx_t變數訪問。乙個事務可能在不同頁上有多個行鎖,因此需要變數trx_locks將乙個事務中的所有行鎖資訊進行鏈結,這樣就可以很快地檢視乙個事務中的所有鎖物件。

通過space/page_no訪問。innodb提供了乙個全域性變數lock_sys_struct來方便查詢行鎖資訊。lock_sys_struct包含乙個hashtable,hash的key是space/page_no,value是鎖物件lock_rec_struct

6. innodb索引組織表的加鎖過程

innodb是通過索引b+樹進行組織的,因此對記錄的加鎖實際上是對索引的加鎖。總的裡說,加鎖流程如下:

(1)通過主鍵進行加鎖的語句,僅對聚焦索引記錄進行加鎖

(2)通過輔助索引進行加鎖的語句,先對輔助索引進行加鎖,再對聚焦索引記錄進行加鎖

(3)通過輔助索引進行加鎖的語句,還可能需要對下乙個輔助索引進行加鎖(需要根據資料庫的隔離級別而定)

mysql 加鎖處理分析

7. 鎖相關的運帷操作

(1)show engine innodb status;

(2)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.requesting_trx_id;

InnoDB之鎖機制

前兩天聽了姜老大關於innodb中鎖的相關培訓,剛好也在看這方面的知識,就順便利用時間把這部分知識做個整理,方便自己理解。主要分為下面幾個部分 1.innodb同步機制 innodb儲存引擎有兩種同步機制選擇,一種是mutex,其是完全的互斥方法。另一種是rw lock,可以給臨界資源加上s lat...

MySQL之InnoDB鎖機制

innodb儲存引擎實現了如下兩種標準的行級鎖 共享鎖 s lock 允許事務讀一行資料。排他鎖 x lock 允許事務刪除或更新一行資料。如果乙個事務 t1 已經獲得了行 r 的共享鎖,那麼另外的事務 t2 可以立即獲得行 r 的共享鎖,因為讀取並沒有改變行 r 的資料,稱這種情況為鎖相容。但若有...

四 InnoDB鎖機制

四 innodb鎖機制 1 表鎖 意向共享鎖 intention shared lock,is 表級意向共享鎖,即表示事務有意向對錶中的某些行加共享s鎖。如select lock in share mode語句,在加行鎖之前會在表上現加is鎖,這樣可以提高鎖衝突檢測的效率,同時也可以避免事務在表級新...