傳統的事務鎖賦予方式是採用FIFS先來先服務的方式,從MySQL8.0.3開始,引入了一種新的模式CATS調度方式,全稱爲Contention-Aware Transaction Scheduling (或者叫做VATS, V=Variance). 顧名思義就是能夠感知到事務競爭關係來實現全局最小開銷的鎖調度方式。
舉個簡單的例子,trx1和trx2同時等待一條記錄鎖,按照傳統的方式,誰先進入等待隊列,誰將優先獲得鎖。但如果同時有2個事務等待trx1,10個事務等待trx2,那麼從全局來看收益最大的顯然是讓trx2獲取到行鎖。
當被掛起等待的事務數超過32個時,會自動切換到新的調度方式
相關資料先列一下:
論文一:
A Top-Down Approach to Achieving Performance Predictability in Database Systems
論文二:
Contention-Aware Lock Scheduling for Transactional Databases
Release Note
-
InnoDB now uses Variance-Aware Transaction Scheduling (VATS) for scheduling the release of transaction locks when the system is highly loaded, which helps reduce lock sys wait mutex contention. Lock scheduling uses VATS when >= 32 threads are suspended in the lock wait queue.
-
For more information about Variance-Aware Transaction Scheduling (VATS), see Identifying the Major Sources of Variance in Transaction Latencies: Towards More Predictable Databases.
WL#10793: InnoDB: Use CATS for scheduling lock release under high load
主要代碼變更見這個commit: fb056f442a96114c74d291302e8c4406c8c8e1af, 或者commit log搜WL#10793關鍵字
這個功能的核心有兩個,一個是如何去維護每個事務的權重,在代碼裏以trx_t::age表示,第二個是基於新的調度算法,如何去選擇被調度的事務。
PS: 本文是本人在看代碼過程中的粗略記錄,不保證全面性
PS2: 本文涉及函數基於MySQL8.0.3
何時使用VATS算法
是否使用新調度算法,需要滿足如下條件(lock_use_fcfs()
):
- 當前線程不是複製線程
- 併發等待線程數超過32(
LOCK_VATS_THRESHOLD
)
關於第二點,增加了lock_sys_t::n_waiting來追蹤,在函數lock_wait_suspend_thread
裏遞增,在lock_wait_table_release_slot
裏遞減
事務權重維護
事務age的接口函數爲lock_update_age
及lock_update_trx_age
,在將新的事務所加入hash,或者完成一次grant操作後,都需要對事務age進行更新
先來看看函數lock_update_age
是如何計算的:
- 如果當前新建的鎖對象不能立刻賦予,需要等待其他鎖對象時,對於已經拿到這個記錄上鎖的事務進行age累加,值爲當前鎖對象事務的trx_t::age + 1,那些等待當前新鎖的事務都需要去依次更新其age;
如果當前事務無需等待,則找到那些在等待該記錄鎖的事務,並累加這些事務的trx_t::age+1 到當前事務 -
針對每個事務的age更新,是一個遞歸函數,函數接口爲
lock_update_trx_age
- a)將新的age值賦予給trx_t
- b)如果當前事務也處於等待狀態的話,則找到其等待的鎖被哪些事務持有,並將age值累加上去。
- 通過如上的遞歸流程,確保了在等待向量圖中每個事務的權重被正確的更新掉
Grant Lock
當釋放掉一個鎖時,需要檢查是否有別的等待的鎖可以獲得鎖,VATS調度的函數入口爲lock_rec_dequeue_from_page --> lock_grant_vats
相比傳統的grant方式,lock_grant_vats
函數的邏輯要複雜許多:
-
將當前鎖對象移除後,剩下的在同一條記錄上的鎖被分爲兩個隊列:
- waiting隊列中存儲需要等待的鎖對象
- granted隊列存儲已經獲的鎖對象
waiting隊列會做一個排序,排序規則從comment裏拷貝的,如下:
-
1. If neither of them is a wait lock, the LHS one has higher priority.
-
2. If only one of them is a wait lock, it has lower priority.
-
3. If both are high priority transactions, the one with a lower seq
-
number has higher priority.
-
4. High priority transaction has higher priority.
-
5. Otherwise, the one with an older transaction has higher priority.
簡單來說,如果不考慮事務優先級,隊列時按照trx_t::age進行排序。
- 然後依次遍歷waiting隊列,如果無需等待(
lock_rec_has_to_wait_vats
), 則賦予記錄鎖,並將其移到哈希隊列的頭部 - 無論是當前釋放的鎖移除出鎖隊列,還是任一等待的事務獲得了鎖,都需要去更新鎖等待圖相關聯事務權重