GaussDB(DWS)中共享訊息佇列實現的三大功能

華為雲開發者社群發表於2021-12-20
摘要:本文將詳細介紹GaussDB(DWS)中共享訊息佇列的實現。

本文分享自華為雲社群《GaussDB(DWS)CBB元件之共享訊息佇列介紹》,作者:瘋狂朔朔。

1)共享訊息佇列是什麼?

在前文中,我們講解了SysCache的實現原理,GaussDB(DWS)通過SysCache快取表後設資料,以加速查詢,然而在併發查詢過程中,不可避免地會出現需要同步後設資料的情況,舉個簡單例子,假設存在以下語句執行流程:

  1. Create table abc(會話1)
  2. Select * from abc(會話1)
  3. Drop table abc(會話2)
  4. Select * from abc(會話1)

在會話1中,會連續兩次執行Select表操作(b和d),在b語句執行後,會話1將對abc的後設資料進行快取,快取到SysCache中,以備後續使用。然而,在c語句執行後,需要對會話1中的後設資料進行失效,否則,會話1將在執行d語句過程中發生錯誤,讀取已刪除的資料。

那麼,會話2如何“通知”會話1失效哪些資料呢?答案是共享訊息佇列。

2)共享訊息佇列儲存結構

如圖所示,為共享訊息佇列資料結構

GaussDB(DWS)中共享訊息佇列實現的三大功能

圖示中主要包括兩部分,下面部分為ThreadLocal結構,主要記錄的是每個執行緒內部的資料,執行緒間資料是獨立的,無法互相訪問,上面部分為共享訊息佇列中的資料,共享訊息佇列存在於共享記憶體中,可同時被多個執行緒訪問,共享訊息佇列的訪問場景是典型的一寫多讀場景。

在共享訊息佇列中,核心變數有三個,nextMsgNum、minMsgNum、MaxMsgNum,其中nextMsgNum記錄了每個執行緒訊息讀取到的位置,minMsgNum記錄了共享訊息佇列中最早訊息的位置,maxMsgNum記錄了共享訊息佇列中最新訊息的位置,對於每個執行緒而言,需要定期(在表加鎖/事務開始/收到訊號時觸發)從共享訊息佇列中讀取失效訊息,利用失效訊息(共享訊息佇列中的每個訊息)更新執行緒內部資料,同時,若執行緒內部產生失效訊息(通常DDL語句在事務提交時產生大量失效訊息),則需要向共享訊息佇列中插入失效資料,供其他執行緒讀取。另外,還有兩個引數,hasMessages、resetState,其中hasMessages用於標記對應執行緒是否存在未讀取的失效訊息,resetState用於標記對應執行緒是否需要失效全部訊息。

NOTE:失效資料有哪些?失效訊息一共有六類(源自PG),有興趣的同學可以研讀PG原始碼,在此處我們不再展開,僅需知道執行緒需要從共享訊息佇列中讀取/插入訊息,以實現資料同步。

3)共享訊息佇列介面實現

共享訊息佇列本質上對於外部介面只需要提供三個功能:讀取共享訊息佇列中訊息、向共享訊息佇列中寫入訊息、清理共享訊息佇列。

3.1)讀取共享訊息佇列

GaussDB(DWS)中共享訊息佇列實現的三大功能

如圖所示,為失效訊息讀取過程。線上程同步失效訊息過程中,有幾個關鍵點:

  • 若共享記憶體中執行緒對應的hasMessage為True,則表示有失效訊息需要讀取,否則直接返回,無新的失效訊息。
  • 讀取失效訊息過程中,需要持有讀共享鎖,以保證讀取的訊息不會被清理掉。
  • 若讀取失效訊息過程中,發現resetState被置為True,說明該執行緒已經無法使用共享訊息佇列中的訊息進行追增,需要對快取進行全失效。快取全失效相當於追增全部資料,需要將nextMsgNum置為maxMsgNum。快取全失效將嚴重降低SQL執行效能,儘量減少快取全失效的發生頻率。
  • 在追增資料過程中,會推進執行緒自身的nextMsgNum,以標記資料追增位置。

NOTE:在讀取共享訊息佇列過程中,每個執行緒推進自己的nextMsgNum位置,以方便記錄資料追增情況。

3.2)向共享訊息佇列中寫入訊息

GaussDB(DWS)中共享訊息佇列實現的三大功能

如圖所示,為失效訊息寫入過程。有以下幾個關鍵點:

  • 寫入失效訊息過程,需要呼叫清理共享訊息佇列介面,以保證有足夠多的空位寫入失效訊息。
  • 在寫入失效訊息過程中,會更新maxMsgNum。
  • 寫入失效訊息完畢以後,需要將其他執行緒的hasMessage標記為True。
  • 寫入失效訊息過程,需要持排他寫鎖,阻塞其他執行緒寫操作,但不阻塞讀。

NOTE:寫入失效訊息過程實際上是推進maxMsgNum的過程,同時告知其他執行緒有新的失效訊息需要讀取。

3.3)清理共享訊息佇列

GaussDB(DWS)中共享訊息佇列實現的三大功能

如圖所示,清理共享訊息佇列過程中,有以下幾個關鍵點:

  • 清理共享訊息佇列需要持有排他讀鎖和排他寫鎖,阻塞讀過程。其主要原因是,清理共享訊息佇列會推進minMsgNum,若不持讀鎖,可能導致nextMsgNum讀取過期資料。
  • 清理共享訊息佇列會推進minMsgNum。
  • 清理共享訊息佇列過程,會將所有沒有及時追增失效訊息的執行緒執行全失效。
  • 在清理共享訊息佇列最後步驟,會對距離minMsgNum最近的執行緒,傳送追增訊號,以確保不會頻繁發生全失效。該步驟主要考慮的情況是,若執行緒長時間處於idle狀態,需要外部訊號觸發其及時追增訊息。

NOTE:清理共享訊息佇列過程,實際上是推進minMsgNum的過程,同時對所有沒有及時追增失效訊息的執行緒執行全失效。

根據以上共享訊息佇列介面可知,讀取共享訊息佇列主要負責推進各個執行緒自身的nextMsgNum;寫入失效訊息主要負責推進maxMsgNum;清理共享訊息佇列主要負責推進minMsgNum。通過共享訊息佇列,可有效實現各個執行緒之間的資料同步。

 

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章