日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

mysql 互斥_MySql中互斥量mutex的实现

發布時間:2025/3/20 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql 互斥_MySql中互斥量mutex的实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

數據庫中的Mutex量指的是一種用于保護一些臨界資源的使用的信號量。當有線程需要使用這些臨界資源時,會請求獲得mutex量,請求成功的線程進入臨界區,而請求失敗的線程只能等待它釋放這個mutex。互斥信號量在計算機軟件層面以上可以看作是實現并發操作的一個原子動作,但在數據庫(操作系統)這種高并發多線程的基礎軟件中,需要精心設計以獲得高吞吐量和良好響應時間。

Mysql中的mutex實現機制大致描述如下(基于Windows操作系統):

數據結構:在mutex結構中,有:

???????? event:操作系統提供的信號量;

???????? lock_word:鎖,這是一個機器字長的數,為1說明mutex有人使用,為0則沒有。

???????? waiters:標志有無人在等待這個mutex,同樣是機器字長的數

?????????list:一個list把系統所有的mutex連起來,這里不用關注

???????? 其它一些debugInfo,不用關注。

同時MySql維護一個全局等待隊列sync_primary_wait_array,在當中有:

???????? array:一個cell數組,每個cell中是一個等待對象的描述,包括等待類型request_type,等待對象指針object、等待線程ID,waiting布爾量表示是否在等待,這個數組在全局等待隊列sync_primary_wait_array初始化時被初始化為系統最大線程數目大小。

???????? os_mutex:一個操作系統提供的互斥量,使用它保護這個全局等待隊列。

???????? 其它信息,不用關注。

當一個線程開始請求一個mutex,它按照以下步驟來進行:

1)TestAndSet,使用一條匯編指令來嘗試獲得這個mutex,這個動作是計算機原子的。

2)如果操作成功,則mutex中的lock_word被置成1,本線程返回,可以進行臨界區操作。

3)如果失敗,開始一個spinLock的過程(所謂spin就是指去扭轉一個門把手,如果扭不開把手會轉回來,然后就繼續去扭直到它開為止,很形象的一個過程,呵呵),不停循環并且讀lock_ward的值,發現為0了(注意,是臟讀,所以去TestAndSet仍然可能失敗)則再次去執行TestAndSet。若置位成功了,則返回。

4)spinLock循環到了一定次數,不能繼續spin下去了,否則太消耗CPU資源,此時本線程睡眠,醒來后再嘗試一次TestAndSet,若成功了,則返回。

5)進入全局等待隊列sync_primary_wait_array中,通過隊列中的os_mutex保證此時在隊列中的操作是唯一的。找到一個空的Cell,把它的對象指針object指向自己,等待線程ID設為自己的ID號,request_type設置為MUTEX,waiting設為FALSE,同時調用操作系統接口reset互斥量mutex上的event(API: ResetEvent),退出全局等待隊列。

6)把mutex中的waiters設為1,標志有人在等待這個mutex,這個動作使用了一個volatile 的指針來改這個值,以保證這個值被立即寫回內存。

7)這時再嘗試去做幾次TestAndSet(垂死掙扎^_^),若成功了,回去把全局等待隊列sync_primary_wait_array中自己剛拿到的cell清空掉,并返回。

8)進入全局等待隊列sync_primary_wait_array,把自己的cell里的waiting標識為TRUE,拿到event后退出全局隊列。

9)等待在這個event上直到被喚醒(API:WaitForSingleObject),被喚醒后立即進入全局等待隊列里放掉自己的cell,再繼續從1)開始。

接下來是線程釋放mutex的步驟:

1)ResetLock,同樣使用一條匯編指令將mutex中的lock_word設置為0;

2)檢查mutex上的waiters是否為1,如果是,則執行下面3)4)兩步的喚醒例程,否則返回。

3)先將waiters設置為0,這同樣使用一個volatile的指針來做。

4)將mutex上的event喚醒(API:SetEvent)。

分析:

MySql的mutex實現依據如下思想:先反復嘗試一小段時間(spin);若不成功,不能繼續無止境地浪費CPU,只好進入休眠;休眠一段時間醒來還是獲得不了,則只好等待釋放mutex的人來將自己喚醒。Mutex的實現需要小心兩種情況:1是出現mutex保護失敗,兩個線程同時獲得了臨界區。在這套實現機制中,明顯在spin和Sleep的過程中獲得的mutex是安全的,因為它們是通過匯編原語來得到的mutex,而一旦進入event等待再被喚醒的人也必須重新使用TestAndSet來獲得mutex,所以這種情況是不可能的。2是某個線程被永遠“遺忘”在獲得mutex的過程內,成為了死線程。在spin和sleep的過程中是不可能的,因為線程會主動醒來不斷嘗試。而線程如果進入了event等待,那么它也一定會在將來被另一個線程喚醒。(但是請注意,這是按我們上面的序列來執行的理論情況,人真的能相信計算機的行為嗎?后面會有答案的)下面先證明這點:

在一個線程A進入event等待前,它做了這樣的步驟:

1.??????? resetEvent

2.??????? 將waiter置為1

3.??????? 再去嘗試幾下

4.??????? 等待在event上

在這個過程結束后,是不可能出現沒有其它任何線程在持有這個mutex而event仍然不是signal狀態的。因為線程A在將waiter置為1后又去嘗試了幾次,這時仍然是得不到的(垂死掙扎是關鍵1)!明顯這時候肯定還有一個線程B在持有這個mutex,而這個線程B在結束的時候,是不可能不看到這個waiter為1的(因為waiter的修改是用了volatile的聲明指針)。這時這個線程B肯定要去調用一次setEvent(關鍵2),從而保證了A線程不可能被“遺忘”。

但是線程在看到waiter為1的時候,是不能保證確實有其它線程在等待event的。考慮這樣一個序列:

???????? 一個線程A請求獲得mutex,在到達步驟6之前,mutex被B持有。

???????? 在A執行6之前的時間,B釋放了mutex,這時B讀到的waiters還是0,B直接返回。

???????? A把waiters設置為1,然后再去垂死掙扎做TestAndSet,這時由于B已經釋放了mutex,A成功,A放掉自己的cell并返回。這時waiter的值為1,而mutex上是沒有人在等待這個event的。當A放mutex的時候,會去喚醒event,但這沒有什么問題,因為當線程發現自己有可能要等待的時候,是會重新在步驟5)里面resetEvent的。

看起來這個mutex是安全的了吧?錯!這個序列行為理論上是安全的,但計算機可未必一定按你想像的去做…在MySql的源代碼中就有一段說明,眾所周知,在超線程的CPU上指令的執行是可能亂序執行的,只要這個亂序行為的指令不具有代碼相關性。在釋放mutex的步驟中的1)和2),這兩條指令就沒有代碼相關性,如果在做1)的動作前2)超前被執行了的話,上面所有的假設都成了水月鏡花……這時候考慮一個序列:

???????? 一個線程A持有著mutex,另一個線程B開始申請。

???????? 在B執行6之前,A先執行了釋放步驟中的2),此時waiters是0,這時A的時間片到了,A被切換。

???????? B一路執行下去,由于A沒有resetLock,B是無法成功了,只好去等待在event上

???????? A回來resetLock,它的堆棧現場里waiters還是0,A瀟灑的離開了。于是B就悲劇地永遠等在了event上,如果再也沒有其它線程來申請這個mutex的話。

為了避免這個情況,MySql只得使用后臺線程來定期檢查這個全局隊列(其實這也是全局隊列需要存在的必要原因),發現有被“遺忘”的線程則去喚醒它。

可見數據庫中的mutex是一個需要考慮硬件實現,并且要考慮多種資源的平衡因素的關鍵性數據結構。但MySql的實現個人認為過于復雜了,Mutex作為一個可以看作是數據庫級別的最輕量級并發控制器,不應該被考慮用于平均時間過長的臨界區。性能才是mutex應該衡量的最關鍵因素。

覺得文章有用?立即:

和朋友一起 共學習 共進步!

猜想失敗,您看看下面的文章有用嗎?

總結

以上是生活随笔為你收集整理的mysql 互斥_MySql中互斥量mutex的实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。