事務的鎖排程
2018-10-11
簡單介紹一下 VLDB 的一篇 paper:ofollow,noindex" target="_blank">Contention-Aware Lock Scheduling for Transactional Databases
問題
多個事務訪問到相同資源的時候,會涉及到加鎖,怎麼樣進行排程,會使得各個事務的延遲儘量短,系統整體的吞吐更高,以及保證排程的公平性?
這個問題是比較難搞的。
- 首先這是一個線上問題 。在事務結束之前,一個鎖將會被持有多久,無法事先預知。
- 其次依賴關係特別複雜 。一個事務可能持有許多的鎖,以及多個事務間可能都依賴同一個鎖。
- 接著是訪問模式的不確定 。不同的事務有不同的訪問模式,有些物件可能是比較 popular 的,被許多事務依賴,不同物件之間並不等價。
- 鎖的模式也有多種類別。比如寫鎖是排它的,而讀鎖是可共享的。
常規的系統大多采用的是 FIFO 的排程方式,也即是哪個事務請求先到達就先執行,並對相應的資源加鎖。paper 裡面指出,FIFO 的策略效果其實是很差的,而它提出的 contention aware 排程策略則相當牛逼。另外,SQL/">MySQL innodb 引擎在 8.0.3 版本以後,預設採用的也是這篇 paper 裡面的方法。
想法
contention-aware 排程的意思是,根據事務整體的鎖競爭會對系統造成的影響,將事務分配優先順序。排程是根據優先順序進行的,而不是按 FIFO 方式。
那麼第一個點就是如何確定鎖競爭的激烈程度,下面是一些直觀的想法。
最多鎖持有優先(MLF)
一個事務,它持有的鎖越多,則越有可能 block 住其它的事務。所以最多鎖持有可以作為一種評判標準。
持有鎖越多的事務,越優先排程。我們將這種方式稱為“最多鎖持有優先”。
這裡的問題在於,物件之間並不是等價的。有些物件很 popular,持有它的鎖會 block 住其它事務,而另一些物件其實並不 block 其它事務。依賴的節點多,並不意味著導致鎖競爭。大部分上的節點都不會發生衝突。只有衝突節點上,才造成鎖競爭。
最多 block 鎖持有優先(MBLF)
事務依賴的鎖,只有當這個鎖還同時被其它事務依賴時,才導致鎖競爭。所以上面的演算法可以優化一下,不使用依賴的節點數量,而使用依賴的鎖數量。
這裡的問題在於,沒有考慮進去鎖的因素,將所有鎖都看作相同的。假設有兩個鎖,其中一個 block 住了一個事務,而另一個鎖 block 住了 10 個事務,在這個評判標準裡面,兩個鎖還是被等同處理的。
依賴子圖深度優先(DDF)
鎖 block 住的事務多了,這個鎖重要性也就提升了。為了抓住鎖之間的重要性不同這個因素,我們可以考慮 “事務依賴的鎖,鎖 block 住的事務” 這一層關係,可以畫一個依賴圖出來。用子圖的深度來衡量整個 block 的程度,也即反映了鎖競爭的程度。
從事務到依賴圖的葉子節點的路徑深度,作為一個事務的優先順序。
最大依賴集優先(LDSF)
因為我們不能考慮事務什麼時候到達,什麼時候結束,所以我們只能考慮當前執行中的事務。考慮事務之前的依賴圖關係。
如果把依賴圖畫出來後,就會發現,“依賴子圖深度優先” 跟 “最多 block 鎖持有優先”,一種是反映圖的垂直方向,一種是圖的水平方向。把它兩結合到一起考慮,就會變成“最大依賴集優先”。
將鎖授予依賴集最大的事務,它能繼續執行往前推進,則意味著依賴集裡面的所有事務,都有潛在推進的可能。這樣最有利於系統整體的向前推進。
修正的最大依賴集優先(bLDSF)
上面的最大依賴集優先是一個簡化了的場景,假定只存在排它鎖。實際上,如果存在共享鎖,則問題會變得複雜一些。
共享鎖會有一個"飢餓"問題,它被一直持有,導致排它鎖加不上去,從而申請排它鎖的事務時間會被拖長,造成系統整體的吞吐下降。所以論文裡面還有一個結論是說,共享鎖並不是分配給越多的事務就越好。
paper 裡面有關於共享鎖的處理細節,這裡先略過。
演算法實現
輸入是依賴圖,依賴物件;輸出是應該將什麼樣的鎖,分配給什麼樣的事務集合。
- 如果還有其它事務仍然物件 o 上面持有鎖,則返回空
- 獲取在物件 o 上面等待共享鎖的集合 {t1,t2...tm}
- 獲取在物件 o 上面等待排它鎖的集合 {ta,tb...tn**
- 計算一個共享鎖的子集 ,使依賴集 {g(t1) |+ g(t2) |+ ...g(tk)} / f(k) 達到最大值
- 取排它鎖的依賴集的最大值
- 如果是從共享鎖依賴集算出來的最大值,大於排它鎖算出來的值,則喚醒共享鎖的一個子集的事務
- 否則,喚醒依賴集最大的那個排它鎖事務
這演算法並不是來一個事務,就給它授予鎖,或者遇到鎖了將它排隊(FIFO)。而是從頭到尾的掃描整個 waiting 的事務,找出一個滿足條件的並授予鎖。
如何計算依賴集大小?
計算依賴集要遍歷子圖,而且每次依賴圖變化了,每個節點要重新實時的計算,這種方式代價太高了。所以paper 裡面是取一個近似值。如果一個事務 t,沒有鎖 block 這個事務,那麼g(t) = 1
。否則,它依賴某些鎖,這些鎖被事務集合t1...tn
佔著,則g(t) = g(t1)+g(t2)...+g(tn) + 1
。這是一個近似演算法,原因是依賴圖不是一個樹,而是一個 DAG,是有節點重疊的。
另外的麻煩的點是算共享鎖的子圖的依賴集情況,先略過了。
大致就這些,至於結果對比...各種吊打 FIFO 策略,就不提了。最後我想質疑一下的是,它的資料場景構造的有一些極端,發論文嘛,動不動就說 100x 提升...真實 case 裡面達不到衝突嚴重到那種程度,所以資料肯定不會有那麼好看。