MariaDB Galera Cluster 是 MariaDB 的複製版本,一個廣受歡迎的 SQL 資料庫。儘管 MariaDB 宣稱 Galera 確保「不遺失交易」,但在至少兩種情況下會遺失交易。首先,在推薦的設定下,系統不會在回應確認前將資料寫入磁碟;當節點快速連續當機時,已提交的交易可能會遺失。其次,在處理程序崩潰和網路分割時,偶爾也會遺失已提交的交易。即使沒有故障,MariaDB Galera Cluster 仍允許 P4(遺失更新)現象,因此無法達到其宣稱介於可序列化與可重複讀取之間的隔離等級。它也經常出現陳舊讀取現象。此研究由 Jepsen 獨立進行,未收取報酬,並遵守 Jepsen 倫理政策。
MariaDB 是一個流行的開源 SQL 資料庫,起源於 MySQL 的分支。MariaDB Galera Cluster 是 MariaDB 的一個主動-主動複製系統,允許每個節點同時讀寫。2015 年,作者分析 MariaDB 與 Galera Cluster,發現雖然 Galera 宣稱提供快照隔離,但 Codership Oy 故意設計系統缺少快照隔離的關鍵屬性「先提交者勝出」,導致在模擬銀行帳戶轉帳工作負載中,MariaDB Galera Cluster 會遺失或憑空產生金錢。2025 年 MariaDB 收購 Codership Oy,將 Galera Cluster 納入 MariaDB 旗下。
Galera Cluster 基於一個稱為 gcomm 的虛擬同步群組通訊框架。交易最初可在任一節點樂觀執行。當交易提交時,會同步複製到其他節點,並根據寫入的主鍵進行認證。交易間的衝突依序列號(seqno)判定。
MariaDB Galera 複製指南稱 Galera 採用一致同意複製:
「與傳統非同步或半同步複製不同,Galera 確保交易在所有節點提交(或全部失敗)後,客戶端才收到成功確認。」
此說法明顯錯誤。若 Galera 真要求所有節點皆提交,將無法容忍單一節點故障。MariaDB 文件多次重複此說法,如「交易未通過所有節點認證前不算真正提交」、「交易提交時,所有節點資料一致」等,但實際上 Galera Cluster 在少數節點故障時仍能運作,符合 MariaDB 關於容錯的說法:只要有法定人數節點在線且連線,系統即可繼續運作。
Galera Cluster 複製指南宣稱「資料在所有節點始終一致,防止節點故障時資料遺失」,並將 Galera 描述為「將多個獨立 MariaDB 伺服器轉變為強健、高可用且一致的分散式資料庫系統」。
此系統理應提供類似強快照隔離的即時一致性模型。MariaDB Galera Cluster 指南稱同步複製確保變更「即時複製到所有節點,無複製延遲且無交易遺失」。此「無交易遺失」說法在官方 README 也有重複。
Galera Cluster 使用指南承諾「標準 SQL 交易(START TRANSACTION、COMMIT、ROLLBACK)如預期運作」。由此可推測 MariaDB Galera Cluster 支援與單節點 MariaDB 相同的一致性模型,但實際情況難以確認。MariaDB Galera 文件中有一節提及已知限制,指出部分顯式鎖定不支援,且必須使用 InnoDB 儲存引擎,但未提及隔離等級或一致性異常。Jepsen 唯一找到的隔離等級說明藏於管理章節的部署提示中,指出「Galera 的 tx_isolation 介於可序列化與可重複讀取之間,tx_isolation 變數被忽略」。
可重複讀取是一個相當強的隔離模型,通常在以主鍵選取物件時等同於可序列化。MariaDB 的「可重複讀取」過去允許非可重複讀取,但現已禁止,理應提供快照隔離。因此預期 MariaDB Galera Cluster 的一致性模型介於可序列化與快照隔離或可重複讀取之間。
Jepsen 使用 MySQL 與 MariaDB 的測試套件,建立三節點 MariaDB Galera Cluster 叢集,運行於 Debian Trixie。安裝 MariaDB 12.1.2 至 12.2.2 版本,Galera 26.4.13 至 26.4.25 版本,並使用 MariaDB 官方 Java 客戶端 3.5.6 版本提交交易。測試中引入多種故障,包括網路分割、程序暫停與終止。
主工作負載使用 Elle 的列表附加檢查器檢驗交易隔離。Elle 依據 Adya 的寫寫、寫讀、讀寫依賴推斷交易間關係,尋找依賴圖中的循環及其他現象。
工作負載隨機生成交易,對唯一主鍵標識的整數列表進行讀取或附加操作。列表以逗號分隔的文字欄位儲存,使用 SQL concat 附加元素。資料分散於多個表中。
由於每筆資料只透過附加唯一整數修改,讀取該列可確定哪些交易寫入及順序,Elle 由此推斷版本順序,進而推斷三種交易資料依賴,並根據歷史的並發結構推斷會話與實時依賴。Elle 找出強連通元件並尋找特定形狀的循環,檢測多種一致性模型違反案例,例如只含寫寫與寫讀邊的循環即為 G1c,違反讀已提交。
當所有節點幾乎同時當機時,MariaDB Galera Cluster 經常遺失已提交交易。例如一分鐘測試中,叢集遺失三列中九個附加值。讀取 112 列時觀察到:
所有寫入這些值的交易均回報成功提交,但叢集重啟後,112 列中 53、56、57、58、71 的附加遺失,取而代之的是 158、159、160 等新元素,遺失元素未在後續讀取中出現。
此現象似乎因設定 innodb_flush_log_at_trx_commit=0 所致,將其設為 1 可大幅降低資料遺失頻率。初選 0 是因 MariaDB 文件中描述此為「較安全且推薦選項」,理由是 Galera Cluster 可透過其他節點恢復不一致。
此設定在非協調故障時有效,但協調故障偶爾發生,如洪水、雷擊、冷卻故障、網路錯誤等,會導致所有節點快速故障,未同步資料可能遺失。Jepsen 將此問題回報為 MDEV-38974。
將 innodb_flush_log_at_trx_commit 設為 1 雖大幅減少資料遺失,但未完全消除。在程序崩潰與網路分割測試中,偶爾仍遺失已提交交易。例如在約 141 秒時,叢集遺失四個物件約十九秒的寫入,部分物件只遺失尾端元素,另有物件完全遺失所有元素並重新開始。
寫入 17、19 等元素的交易均成功提交,理應不會遺失。此問題每幾小時測試才出現一次,對生產環境影響可能有限,但仍令人擔憂,Jepsen 已回報為 MDEV-38976。
即使未發生寫入遺失,Galera Cluster 仍允許 P4(遺失更新)及其他 G-single 異常,且在健康叢集中無故障時也會出現。例如某測試中,兩筆交易分別對鍵 468 進行讀取與附加操作,後續讀取結果顯示資料順序異常,暗示後交易在前交易讀取與寫入間修改資料,屬典型遺失更新。
遺失更新違反快照隔離,且因操作均以主鍵存取,亦違反可重複讀取。Jepsen 亦觀察到更複雜的多鍵、多交易循環,均屬 G-single,違反快照隔離與可重複讀取,已回報為 MDEV-38977。
最後,正常運作下 MariaDB Galera Cluster 偶爾出現陳舊讀取:一交易提交並回報成功後,後續交易未能觀察到前者寫入。例如某測試中,先交易對鍵 17693 附加 9 並提交,後交易開始後讀取該鍵卻未見該附加,屬陳舊讀取,與 Galera Cluster 宣稱的即時無延遲複製不符。
此現象每幾分鐘出現一次,無需故障注入,Jepsen 已回報為 MDEV-38999。
MariaDB Galera Cluster 宣稱提供介於可序列化與可重複讀取間的隔離等級,且交易「即時複製至所有節點,確保無複製延遲與交易遺失」。但在推薦設定下,當多節點快速故障時會遺失已提交交易,且在程序崩潰與網路分割時偶爾遺失交易。健康叢集中亦出現遺失更新與陳舊讀取,未達快照隔離、可重複讀取或其強化版本。交易遺失甚至暗示其一致性弱於讀未提交。
用戶應將 innodb_flush_log_at_trx_commit 設為 1 以降低協調故障時寫入遺失風險。MariaDB 應修訂文件,明確指出設為 0 會導致 Galera Cluster 資料遺失。
即使設為 1,節點故障與網路分割時仍可能遺失已提交寫入,幸好此情況不常見。Galera Cluster 亦在無故障時出現陳舊讀取、遺失更新及其他 G-single 異常。交易可能在自身讀寫間被其他交易修改,使用讀-改-寫模式(如多數 ORM)可能不安全。用戶應假設已提交交易不一定對後續交易可見。
MariaDB 文件難以判斷 Galera Cluster 支援何種一致性模型。理論上應提供強快照隔離或強可重複讀取,但實際上似乎弱於讀未提交。建議 MariaDB 更新文件,明確說明 Galera Cluster 的實際一致性模型。
本結果來自對 Galera Cluster 的初步探索,可能還有其他未揭露行為。Jepsen 採實驗方法驗證安全性:能證明錯誤存在,卻無法證明無錯誤。雖努力尋找問題,仍無法保證正確性。
測試中使用 CONCAT 附加字串,推測 MariaDB Galera Cluster 對盲寫寄存器亦會出現遺失更新,可能無法通過先前 Jepsen 模擬銀行工作負載測試,金錢可能被毀損或憑空產生。尚未探討謂詞查詢、慢網路、時鐘偏差或磁碟故障,未來研究可從這些方向深入。
Jepsen 感謝 MariaDB 郵件列表的 Gordan Bobic 與 Teemu Ollakka,及 Irene Kannyo 的編輯協助。此研究由 Jepsen 獨立完成,未收取報酬,並遵守 Jepsen 倫理政策。