MySQL原始碼學習 MDL字典鎖

2021-06-28 19:25:11 字數 4229 閱讀 4758

什麼是mdl

mdl,meta data lock,元資料鎖,一般稱為字典鎖。字典鎖與資料鎖相對應。字典鎖是為了保護資料物件被改變,一般是一些ddl會對字典物件改變,如兩個tx,tx1先查詢表,然後tx2試圖drop,字典鎖就會lock住tx2,直到tx1結束(提交或回滾)。資料鎖是保護表中的資料,如兩個tx同時更新一行時,先得到row lock的tx會先執行,後者只能等待。

mdl的設計目標

字典鎖在設計的時候是為了資料庫物件的元資料。到達以下3個目的。

1. 提供對併發訪問記憶體中字典物件快取(table definatin cache,tdc)的保護。這是系統的內部要求。

2. 確保dml的併發性。如tx1對錶t1查詢,tx2同是對錶t1插入。

3. 確保一些操作的互斥性,如dml與大部分ddl(alter table除外)的互斥性。如tx1對錶t1執行插入,tx2執行drop table,這兩種操作是不允許併發的,故需要將表物件保護起來,這樣可以保證binlog邏輯的正確性。(貌似之前的版本存在字典鎖是語句級的,導致 binlog不合邏輯的bug。)

支援的鎖型別

資料庫理論中的基本鎖型別是s、x,意向鎖is、ix是為了層次上鎖而引入的。比如要修改表中的資料,可能先對表上乙個表級ix鎖,然後再對修改的資料上乙個行級x鎖,這樣就可以保證其他試圖修改表定義的事物因為獲取不到表級的x鎖而等待。

mysql中將字典鎖的型別根據不同語句的功能,進一步細分,細分的依據是對字典的操作和對資料的操作。細分的好處是能在一定程度上提高併發效率,因為如果只定義x和s兩種鎖,必然導致相容性矩陣的侷限性。mysql不遺餘力的定義了如下的鎖型別。

名稱意義

mdl_intention_exclusive

意向排他鎖,只用於範圍上鎖

mdl_shared

共享鎖,用於訪問字典物件,而不訪問資料。

mdl_shared_high_prio

只訪問字典物件(如desc table)

mdl_shared_read

共享讀鎖,用於讀取資料(如select)

mdl_shared_write

共享寫鎖,用於修改資料(如update)

mdl_shared_no_write

共享非寫鎖,允許讀取資料,阻塞其他tx修改資料(如alter table)

mdl_shared_no_read_write

用於訪問字典,讀寫資料

不允許其他tx讀寫資料

mdl_exclusive

排他鎖,可以修改字典和資料

可以看到mysql在alter table的時候還是允許其他事務進行讀表操作的。需要注意的是讀操作的事物需要在alter table獲取mdl_shared_no_write鎖之後,否則無法併發。這種應用場景應該是對乙個較大的表進行alter時,其他事物仍然可以讀,併發性得到了提高。

鎖的相容性

鎖的相容性就是我們經常看到的那些相容性矩陣,x和s必然互斥,s和s相容。mysql根據鎖的型別我們也可以知道其相容矩陣如下:ixs

shsr

swsnw

snrwxix

1111

1111

s111

1111

0sh11

1111

10sr1

1111

100sw

1111

1000

snw111

1000

0snrw11

1000

00x1

0000

0001代表相容,0代表不相容。你可能發現x和ix竟然相容,沒錯,其實這裡的ix已經不是傳統意義上的ix,這個ix是用在範圍鎖上,所以和x鎖不互斥。

資料結構

涉及到的和鎖相關的資料結構主要是如下幾個:

mdl_context:字典鎖上下文。包含乙個事物所有的字典鎖請求。

mdl_request:字典鎖請求。包含對某個物件的某種鎖的請求。

mdl_ticket:字典鎖排隊。mdl_request就是為了獲取乙個ticket。

mdl_lock:鎖資源。乙個物件全域性唯一。可以允許多個可以併發的事物同時獲得。

涉及到的原始碼檔案主要是sql/mdl.cc

鎖資源

鎖資源在系統中是共享的,即全域性的,存放在static mdl_map mdl_locks;的hash鍊錶中,對於資料庫中的乙個物件,其hashkey必然是唯一的,對應乙個鎖資源。多個事務同時對一張表操作時,申請的 lock也是同乙個記憶體物件。獲取mdl_locks中的lock需要通過全域性互斥量保護起來 mysql_mutex_lock(&m_mutex); m_mutex是mdl_map的成員。

上鎖流程

乙個會話連線在實現中對應乙個thd實體,乙個thd對應乙個mdl_context,表示需要的mdl鎖資源,乙個mdl_context中包含多個mdl_request,乙個mdl_request即是對乙個物件的某種型別的lock請求。每個mdl_request上有乙個ticket物件,ticket中包含lock。

上鎖的也就是根據mdl_request進行上鎖。

acquire_lock:  

if (mdl_request 

contains

the needed ticket )  

return

ticket;  

endif;  

create

a ticket;  

if (!find lock 

inlock_sys)  

create

a lock;  

endif  

if (lock can be granted 

tomdl_request)  

setlock 

toticket;  

setticket 

tomdl_request;  

else

wait 

forlock  

endif 

稍微解釋下,首先是在mdl_request本身去檢視有沒有相等的或者stronger的ticket,如果存在,則直接使用。否則建立乙個 ticket,查詢上鎖物件對應的lock,沒有則建立。檢查lock是否可以被賦給本事務,如果可以直接返回,否則等待這個lock;

鎖等待與喚醒

字典物件的鎖等待是發生在兩個事物對同一物件上不相容的鎖導致的。當然,由於lock的唯一性,先到先得,後到的只能等待。

如何判斷乙個lock是否可以grant給乙個tx?這需要結合lock結構來看了,lock上有兩個成員,grant和wait,grant代表此 lock允許的事物都上了哪些鎖,wait表示等待的事務需要上哪些鎖。其判斷乙個事物是否可以grant的邏輯如下:

if(compatible(lock.

grant

, tx.locktype))  

if (compatible(lock.wait, tx.locktype))  

return

can_grant;  

endif  

endif 

即首先判斷grant中的鎖型別和當前事務是否相容,然後判斷wait中的鎖型別和當前事務是否相容。細心的話,會想到,wait中的鎖型別是不需要和當前事務進行相容性比較的,這是不是說這個比較是多餘的了?其實也不是,因為wait的相容性矩陣和上面的矩陣是不一樣的,wait的相容性矩陣感覺是在 ddl等待的情況下,防止dml繼續進來(wait矩陣就不寫出來了,大家可以去**裡看下)。

比如:tx1                    tx2                    tx3

select t1

drop  t1

select t1

這時候tx2會阻塞,tx3也會阻塞,被tx2阻塞,也就是說被wait的事件阻塞了,這樣可能就是為了保證在ddl等待時,禁止再做dml了,因為在ddl面前,dml顯得確實不是那麼重要了。

如何喚醒被等待的事務呢?比如喚醒tx2,當tx1結束時,會呼叫release_all_locks_for_name,對被鎖住的事務進行喚醒,具體操作封裝在reschedule_waiters函式中,重置等待時間的標記位進行喚醒,重點**如下:

if (can_grant_lock(ticket->get_type(), ticket->get_ctx()))  

今天把mdl系統總體上看了一下,對鎖的請求、等待以及喚醒有了初步了解。併發性的問題是最難除錯的,大家如果想做鎖方面的實驗,可以利用vs除錯中的凍結執行緒的功能,這樣就可以確保併發情況控制完全按照你設計思路去呈現。

mysql原始碼公升級 MySQL原始碼公升級

mysql原始碼公升級 公升級的方法一般有兩類 1.利用mysqldump來直接匯出sql檔案,匯入到新庫中,這種方法是最省事兒的,也是最保險的,缺點的話,也顯而易見,大庫的mysqldump費時費力。2.直接替換掉mysql的安裝目錄和my.cnf,利用mysql upgrade 來完成系統表的公...

原始碼安裝 mysql 原始碼安裝 mysql

yum y install ncurses devel gcc bzip2 bison 安裝解壓命令 安裝命令 wget http 複製的鏈結.解壓命令 tar xf cmake檔案.tar 編譯工具cmake安裝 cmake命令版本 2.8以上 將資料夾放到指定目錄整理 mv usr local ...

mysql 原始碼文件 mysql原始碼安裝官方文件

以下是安裝mysql原始碼分發版的更詳細的描述 1.為mysqld增加乙個登入使用者和組 2.shell groupadd mysql3.shell useradd g mysql mysql 這些命令將增加mysql組和mysql使用者。不同版本的unix中,useradd和groupadd的語法...