mysql 互斥_MySql中互斥量mutex的实现
數(shù)據(jù)庫中的Mutex量指的是一種用于保護一些臨界資源的使用的信號量。當(dāng)有線程需要使用這些臨界資源時,會請求獲得mutex量,請求成功的線程進入臨界區(qū),而請求失敗的線程只能等待它釋放這個mutex。互斥信號量在計算機軟件層面以上可以看作是實現(xiàn)并發(fā)操作的一個原子動作,但在數(shù)據(jù)庫(操作系統(tǒng))這種高并發(fā)多線程的基礎(chǔ)軟件中,需要精心設(shè)計以獲得高吞吐量和良好響應(yīng)時間。
Mysql中的mutex實現(xiàn)機制大致描述如下(基于Windows操作系統(tǒng)):
數(shù)據(jù)結(jié)構(gòu):在mutex結(jié)構(gòu)中,有:
???????? event:操作系統(tǒng)提供的信號量;
???????? lock_word:鎖,這是一個機器字長的數(shù),為1說明mutex有人使用,為0則沒有。
???????? waiters:標(biāo)志有無人在等待這個mutex,同樣是機器字長的數(shù)
?????????list:一個list把系統(tǒng)所有的mutex連起來,這里不用關(guān)注
???????? 其它一些debugInfo,不用關(guān)注。
同時MySql維護一個全局等待隊列sync_primary_wait_array,在當(dāng)中有:
???????? array:一個cell數(shù)組,每個cell中是一個等待對象的描述,包括等待類型request_type,等待對象指針object、等待線程ID,waiting布爾量表示是否在等待,這個數(shù)組在全局等待隊列sync_primary_wait_array初始化時被初始化為系統(tǒng)最大線程數(shù)目大小。
???????? os_mutex:一個操作系統(tǒng)提供的互斥量,使用它保護這個全局等待隊列。
???????? 其它信息,不用關(guān)注。
當(dāng)一個線程開始請求一個mutex,它按照以下步驟來進行:
1)TestAndSet,使用一條匯編指令來嘗試獲得這個mutex,這個動作是計算機原子的。
2)如果操作成功,則mutex中的lock_word被置成1,本線程返回,可以進行臨界區(qū)操作。
3)如果失敗,開始一個spinLock的過程(所謂spin就是指去扭轉(zhuǎn)一個門把手,如果扭不開把手會轉(zhuǎn)回來,然后就繼續(xù)去扭直到它開為止,很形象的一個過程,呵呵),不停循環(huán)并且讀lock_ward的值,發(fā)現(xiàn)為0了(注意,是臟讀,所以去TestAndSet仍然可能失敗)則再次去執(zhí)行TestAndSet。若置位成功了,則返回。
4)spinLock循環(huán)到了一定次數(shù),不能繼續(xù)spin下去了,否則太消耗CPU資源,此時本線程睡眠,醒來后再嘗試一次TestAndSet,若成功了,則返回。
5)進入全局等待隊列sync_primary_wait_array中,通過隊列中的os_mutex保證此時在隊列中的操作是唯一的。找到一個空的Cell,把它的對象指針object指向自己,等待線程ID設(shè)為自己的ID號,request_type設(shè)置為MUTEX,waiting設(shè)為FALSE,同時調(diào)用操作系統(tǒng)接口reset互斥量mutex上的event(API: ResetEvent),退出全局等待隊列。
6)把mutex中的waiters設(shè)為1,標(biāo)志有人在等待這個mutex,這個動作使用了一個volatile 的指針來改這個值,以保證這個值被立即寫回內(nèi)存。
7)這時再嘗試去做幾次TestAndSet(垂死掙扎^_^),若成功了,回去把全局等待隊列sync_primary_wait_array中自己剛拿到的cell清空掉,并返回。
8)進入全局等待隊列sync_primary_wait_array,把自己的cell里的waiting標(biāo)識為TRUE,拿到event后退出全局隊列。
9)等待在這個event上直到被喚醒(API:WaitForSingleObject),被喚醒后立即進入全局等待隊列里放掉自己的cell,再繼續(xù)從1)開始。
接下來是線程釋放mutex的步驟:
1)ResetLock,同樣使用一條匯編指令將mutex中的lock_word設(shè)置為0;
2)檢查mutex上的waiters是否為1,如果是,則執(zhí)行下面3)4)兩步的喚醒例程,否則返回。
3)先將waiters設(shè)置為0,這同樣使用一個volatile的指針來做。
4)將mutex上的event喚醒(API:SetEvent)。
分析:
MySql的mutex實現(xiàn)依據(jù)如下思想:先反復(fù)嘗試一小段時間(spin);若不成功,不能繼續(xù)無止境地浪費CPU,只好進入休眠;休眠一段時間醒來還是獲得不了,則只好等待釋放mutex的人來將自己喚醒。Mutex的實現(xiàn)需要小心兩種情況:1是出現(xiàn)mutex保護失敗,兩個線程同時獲得了臨界區(qū)。在這套實現(xiàn)機制中,明顯在spin和Sleep的過程中獲得的mutex是安全的,因為它們是通過匯編原語來得到的mutex,而一旦進入event等待再被喚醒的人也必須重新使用TestAndSet來獲得mutex,所以這種情況是不可能的。2是某個線程被永遠“遺忘”在獲得mutex的過程內(nèi),成為了死線程。在spin和sleep的過程中是不可能的,因為線程會主動醒來不斷嘗試。而線程如果進入了event等待,那么它也一定會在將來被另一個線程喚醒。(但是請注意,這是按我們上面的序列來執(zhí)行的理論情況,人真的能相信計算機的行為嗎?后面會有答案的)下面先證明這點:
在一個線程A進入event等待前,它做了這樣的步驟:
1.??????? resetEvent
2.??????? 將waiter置為1
3.??????? 再去嘗試幾下
4.??????? 等待在event上
在這個過程結(jié)束后,是不可能出現(xiàn)沒有其它任何線程在持有這個mutex而event仍然不是signal狀態(tài)的。因為線程A在將waiter置為1后又去嘗試了幾次,這時仍然是得不到的(垂死掙扎是關(guān)鍵1)!明顯這時候肯定還有一個線程B在持有這個mutex,而這個線程B在結(jié)束的時候,是不可能不看到這個waiter為1的(因為waiter的修改是用了volatile的聲明指針)。這時這個線程B肯定要去調(diào)用一次setEvent(關(guān)鍵2),從而保證了A線程不可能被“遺忘”。
但是線程在看到waiter為1的時候,是不能保證確實有其它線程在等待event的。考慮這樣一個序列:
???????? 一個線程A請求獲得mutex,在到達步驟6之前,mutex被B持有。
???????? 在A執(zhí)行6之前的時間,B釋放了mutex,這時B讀到的waiters還是0,B直接返回。
???????? A把waiters設(shè)置為1,然后再去垂死掙扎做TestAndSet,這時由于B已經(jīng)釋放了mutex,A成功,A放掉自己的cell并返回。這時waiter的值為1,而mutex上是沒有人在等待這個event的。當(dāng)A放mutex的時候,會去喚醒event,但這沒有什么問題,因為當(dāng)線程發(fā)現(xiàn)自己有可能要等待的時候,是會重新在步驟5)里面resetEvent的。
看起來這個mutex是安全的了吧?錯!這個序列行為理論上是安全的,但計算機可未必一定按你想像的去做…在MySql的源代碼中就有一段說明,眾所周知,在超線程的CPU上指令的執(zhí)行是可能亂序執(zhí)行的,只要這個亂序行為的指令不具有代碼相關(guān)性。在釋放mutex的步驟中的1)和2),這兩條指令就沒有代碼相關(guān)性,如果在做1)的動作前2)超前被執(zhí)行了的話,上面所有的假設(shè)都成了水月鏡花……這時候考慮一個序列:
???????? 一個線程A持有著mutex,另一個線程B開始申請。
???????? 在B執(zhí)行6之前,A先執(zhí)行了釋放步驟中的2),此時waiters是0,這時A的時間片到了,A被切換。
???????? B一路執(zhí)行下去,由于A沒有resetLock,B是無法成功了,只好去等待在event上
???????? A回來resetLock,它的堆棧現(xiàn)場里waiters還是0,A瀟灑的離開了。于是B就悲劇地永遠等在了event上,如果再也沒有其它線程來申請這個mutex的話。
為了避免這個情況,MySql只得使用后臺線程來定期檢查這個全局隊列(其實這也是全局隊列需要存在的必要原因),發(fā)現(xiàn)有被“遺忘”的線程則去喚醒它。
可見數(shù)據(jù)庫中的mutex是一個需要考慮硬件實現(xiàn),并且要考慮多種資源的平衡因素的關(guān)鍵性數(shù)據(jù)結(jié)構(gòu)。但MySql的實現(xiàn)個人認為過于復(fù)雜了,Mutex作為一個可以看作是數(shù)據(jù)庫級別的最輕量級并發(fā)控制器,不應(yīng)該被考慮用于平均時間過長的臨界區(qū)。性能才是mutex應(yīng)該衡量的最關(guān)鍵因素。
覺得文章有用?立即:
和朋友一起 共學(xué)習(xí) 共進步!
猜想失敗,您看看下面的文章有用嗎?
總結(jié)
以上是生活随笔為你收集整理的mysql 互斥_MySql中互斥量mutex的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql5.7配置_MySQL5.7详
- 下一篇: mysql qps计算方法_mysql计