分散式事務處理兩階段提交機制和原理

KunlunDB發表於2022-01-17
一、背景


筆者在上篇文章中回顧了經典的兩階段提交演算法原理及缺陷,有興趣可點選檢視原文《 「技術討論」經典的兩階段提交演算法原理及缺陷 》,此篇不做詳述。

為避免經典的兩階段提交演算法缺陷的發生,崑崙分散式資料庫的分散式事務處理機制基於經典的兩階段提交演算法,並在此基礎上增強了其容災能力和錯誤處理能力。

故此可以做到任意時刻崑崙資料庫叢集的任意節點當機或者網路故障、超時等都不會導致叢集管理的資料發生不一致或者丟失等錯誤。

二、崑崙資料庫如何分散式事務處理兩階段提交?

崑崙資料庫分散式事務處理功能涉及的模組分佈在 計算節點,儲存叢集和後設資料叢集和 cluster_mgr 模組中(如圖 1

2.1  計算節點模組

計算節點包含全域性事務管理器 Global Transaction Manager GTM ),它掌握著一個計算節點中正在執行的每一個客戶端連線(即 Session ,會話)中正在執行的分散式事務 GT 的內部狀態, 關鍵資訊包括事務 GT 讀寫了哪些儲存叢集( storage shard) ,以及全域性事務 ID 等。

下圖 (如圖 1 中的 GT1 GT2 內部狀態為:

  • GT1 在儲存叢集 1 上執行的事務分支 T11 做了讀寫操作,在儲存叢集 2 上執行的事務分支 T12 做了寫入操作。

  • GT2 在儲存叢集 1 上執行的事務分支 T21 做了只讀操作,在儲存叢集 2 上執行的事務分支 T22 做了寫入操作。


計算節點的GTSS 後臺程式負責成組批次寫入全域性事務的commit log 日誌到後設資料叢集中。

崑崙資料庫會確保每一個記錄了Commit log 的全域性事務GT ,都一定會完成提交。 具體的兩階段提交流程見下文詳述,本節先把相關模組介紹完。

2.2  後設資料叢集 模組

後設資料叢集也是一個高可用的MySQL  叢集,它的commit log 記錄著每一個兩階段提交的事務的提交決定。

這些提交決定是給cluster_mgr 做錯誤處理使用的,實際生產系統場景下極少會真的用到,但是其資訊非常關鍵。

只有當計算節點或者儲存節點發生當機、斷電等故障和問題時,才會被cluster_mgr 用來處理殘留的prepared 狀態的事務分支。

2.3  儲存叢集 模組

儲存叢集是一個MySQL 在儲存叢集中,mysql 的會話(THD )物件內部,包含分散式事務分支(簡稱XA 事務)的狀態:

  • 在下圖 (如圖 1 中儲存節點1 包含分散式事務GT1 的事務分支GT1.T11 GT2 的事務分支GT2.T21 的本地執行狀態。

  • 在下圖 (如圖1)中儲存節點2 包含分散式事務GT1 的事務分支GT1.T12 GT2 的事務分支GT2.T22 的本地執行狀態。


2.4  cluster_mgr 模組

cluster_mgr 是一個獨立程式,藉助後設資料叢集中的後設資料,與儲存叢集和計算節點互動,輔助它們工作。

在分散式事務處理這個場景下,它負責處理因為計算節點和/ 或儲存節點當機而殘留的prepared 狀態的事務分支,根據每個事務分支所屬的全域性事務的commit log 來決定提交或者回滾其事務分支(具體會在下文詳述)。

分散式事務處理兩階段提交機制和原理

1.  崑崙資料庫分散式事務處理涉及的功能模組和元件


三、如何實現兩階段提交?


在使用者傳送begin transaction 給計算節點時,計算節點會在其內部開啟一個新的分散式事務GT 物件(GTM 會為這個分散式事務GT 建立內部狀態)。

然後在GT 事務執行期間首次讀寫一個儲存叢集時,GTM 會傳送包含XA START 在內的若干條SQL 語句啟動GT 在這個儲存叢集中的事務分支,並初始化事務狀態。

然後傳送DML 語句來讀寫資料。計算節點會對收到的SQL 語句做解析、最佳化、執行並計算應該向哪些目標分片傳送什麼樣的SQL 語句完成區域性資料讀寫工作,只讀寫確實含有目標資料的儲存叢集。

計算節點與一組儲存節點的通訊總是做非同步通訊,確儲存儲節點併發執行SQL語句( 不過這部分內容不是本文討論的重點)。      

在一個分散式事務GT執行commit之前如果發生了崑崙資料庫叢集中的節點、網路故障或者儲存節點的部分SQL語句執行出錯,那麼計算節點的GTM會回滾事務GT及其在儲存節點上的所有事務分支,GT相當於沒有被執行過,它不會對使用者資料造成任何影響。

下面詳述計算節點執行客戶端傳送commit的語句的分散式事務提交流程。事務提交的正常情況流程(時序圖)見圖2。

分散式事務處理兩階段提交機制和原理

2. 兩階段提交正常執行的時序圖


3.1  第一階段


當客戶端/ 應用傳送commit 語句時,GTM 根據分散式事務GT 的內部狀態選擇提交策略 (當GT 寫入的儲存叢集少於2 個時,對GT 訪問過的所有儲存叢集執行一階段提交)

MySQL 中這個SQL 語句是XA COMMIT ... ONE PHASE ;在分散式事務做第一階段提交過程中如果發生任意節點當機,那麼這些節點本地完成恢復即可正常工作,使用者資料不會錯亂、不一致。

具體來說,如果當機的節點包含那個唯一做過寫入的節點WN,那麼WN完成本地恢復後,如果GT在WN的事務分支TX被恢復了,那麼GT的全部改動(全部在TX中)就是生效的,否則GT的全部改動(全部在TX中)就沒有生效(無論如何GT的原子性都是保持的)。

如果當機的節點全部都是GT的只讀節點那麼GT的任何改動都沒有丟失,也不會造成GT的狀態出錯或者資料不一致。

執行只讀事務分支的儲存節點重啟並完成恢復後,那些之前執行中的只讀事務殘留的undo log都會被InnoDB自動清除,其他之前執行時的內部狀態全部在記憶體中,隨著重啟已經都消失了,因此完全可以忽略只讀事務一階段提交的任何錯誤。

所以這種情況下,對其唯一的寫入節點的commit語句可以正常繼續執行。

GT 寫入的儲存叢集不少於2 個時 GTM GT 寫入過的所有儲存叢集執行兩階段提交,並且對GT 只讀訪問過的每個儲存叢集執行一階段提交。

執行兩階段提交時,第一階段全部返回成功後才會執行第二階段的提交(XA COMMIT)  命令,否則第二階段會執行XA ROLLBACK 命令回滾所有兩階段提交的事務分支。


3.2  如何批次寫Commit log?


在開始第二階段提交之前,GTM 會請求GTSS 程式為每個GT 寫入commit log 並且等待其成功返回。

只有成功為GT 寫入commit log GTM 才會對GT 開始第二階段提交,否則直接回滾這些prepared 狀態的事務分支

GTM 在每個後端程式(backend process PostgreSQL 術語,也就是執行一個使用者連線中的SQL 語句的程式,每個使用者連線繫結一個後端程式)中會把每個要開始第二階段提交的分散式事務的ID 等關鍵資訊放入GTSS 的請求佇列然後等待GTSS 通知請求完成。

GTSS 會把請求佇列中所有的commit log 寫入請求轉換為一條SQL insert 語句發給後設資料叢集,該叢集執行insert 語句完成commit logging 並向GTSS 確認成功,然後GTSS 即可通知每一個等待著的後端程式開始第二階段提交。

如果commit log 寫入失敗那麼計算節點會傳送回滾命令(XA ROLLBACK)  讓儲存叢集回滾GT 的事務分支。

如果commit log  寫入超時那麼計算節點會斷開與儲存叢集的連線以便讓cluster_mgr 事後處理。

所有確認寫入commit log 的分散式事務一定會完成提交,如果發生計算節點或者儲存節點故障或者網路斷連等,那麼cluster_mgr  模組會按照commit log  的指令來處理這些prepared  狀態的事務分支。


3.3  後設資料節點會不會成為效能瓶頸?


一定會有讀者擔心,把所有計算節點發起的分散式事務的commit log 寫到同一個後設資料叢集中, 那麼後設資料叢集會不會成為效能瓶頸?會不會出現單點依賴?

經過驗證並與我們的預期相符的是: 100 TPS 的極高吞吐率情況下,後設資料叢集也完全不會成為效能瓶頸。

具體來說,在1000 個連線的sysbench 測試滿負荷執行時,GTSS 批次寫入commit log 的這個組的規模通常在200 左右,其他的工作負載以及相關引數配置(預設cluster_commitlog_group_size = 8  cluster_commitlog_delay_ms=10 )下,這個規模可能更大或者更小。

考慮到每行commit log 資料量不到20 個位元組(是與工作負載無關的固定長度),也就是200 個儲存叢集的分散式事務會導致後設資料叢集執行一個寫入約4KB WAL 日誌的事務,那麼即使叢集整體TPS 達到100 萬每秒,後設資料叢集也只有5 TPS ,每秒寫入20MB WAL 日誌,對於現在的SSD 儲存裝置來說是九牛一毛,完全可以負擔的。

所以即使儲存叢集滿負荷執行,後設資料叢集的寫入負載仍然極低(後設資料叢集不會成為崑崙資料庫叢集的效能瓶頸)

GTSS 會最多等待cluster_commitlog_delay_ms 毫秒以便收集至少cluster_commitlog_group_size 個事務批次傳送給後設資料叢集。

透過調整這兩個引數可以在commit log 組規模和事務提交延時之間取得平衡。


3.4  第二階段


commit log 寫入成功後,GTSS 程式會通知所有等待其commit log 寫入結果的使用者會話(連線)程式,這些程式就可以開始第二階段提交了。

第二階段中,GTM向當前分散式事務GT寫入過的每個儲存節點並行非同步傳送提交(XA COMMIT)命令,然後等待這些節點返回結果。

無論結果如何(斷連,儲存節點故障),GTM都將返回成功給使用者。因為第二階段開始執行就意味著這個事務一定會完成提交。

甚至如果第二階段進行過程中計算節點當機或者斷網了那麼這個事務仍將提交,此時應用系統後端(也就是資料庫的客戶端)會發現自己的commit語句沒有返回直到資料庫連線超時(通常應用層也會讓終端使用者連線超時)或者返回了斷連錯誤。


四、 總結


筆者和團隊在崑崙分散式資料庫中的兩階段提交方式,可以成功避免經典的兩階段提交演算法的缺陷。


而在此分散式事務處理兩階段提交機制和原理上,筆者和 增強其容災能力和錯誤處理能力,可以做到 任意時刻崑崙資料庫叢集的任意節點當機或者網路故障、超時等都不會導致叢集管理的資料發生不一致或者丟失等錯誤。

下篇文章詳述《分散式事務對於兩階段提交的錯誤處理》~

推薦閱讀






來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70011764/viewspace-2852621/,如需轉載,請註明出處,否則將追究法律責任。

相關文章