🗄️ MySQL / MariaDB Active-Active 高可用架構:雙主同步 + 自動故障切換完整指南
當網站或系統成長到一定規模,「一台資料庫 + 一台備援」的 Active-Passive 架構,很快就會遇到極限: 讀寫都擠在同一台主機、備援平時閒置、故障切換還可能需要人工介入。 這時候,「Active-Active 雙主+自動故障切換」會是自然的下一步。 本文以 MySQL / MariaDB 為主,從概念、架構選型、部署實作到故障情境與排錯整理一套完整實務指南。
📌 一、Active-Active 是什麼?與 Active-Passive 有何不同?
先用最簡單的方式,把兩種常見架構拆開來看:
- Active-Passive(主備):
一台主庫處理所有讀寫,另一台備庫透過複寫(同步或非同步)跟上主庫。平時備庫不對外服務,只有主庫掛掉時才切換。 - Active-Active(雙主 / 多主):
多個節點 同時對外提供服務,流量可以被分散到不同 DB,理論上可以同時提升吞吐與可用性。
兩者最關鍵的差異在於:
- 資料一致性難度:Active-Active 必須處理「多個節點同時寫入同一筆資料」的衝突問題。
- 流量分散方式:Active-Passive 基本上所有寫入都打到同一台;Active-Active 則需要搭配 Load Balancer / 中介層 管流量。
- 故障行為:Active-Passive 只有一個「主」需要切換;Active-Active 則要處理「某個節點死掉後,如何快速把流量導向其他健康節點」。
因此,Active-Active 不是多開幾台 DB、互相做主從 就能完成。 你需要的是:支援多主架構的複寫技術+設計良好的流量入口+故障偵測機制。
📚 二、常見 MySQL / MariaDB Active-Active 架構選項總覽
實務上,想在 MySQL / MariaDB 世界達成 Active-Active,常見選項大致如下:
1. 傳統雙主複寫(Master-Master Replication)— ❌ 不建議作為主力
這是很多人最先想到的做法:
- A 主 <=> B 主:互相做主從複寫,兩邊都可以寫。
- 在應用程式端或 Proxy 端,同時把流量送到兩邊。
問題是:
- 衝突處理很麻煩:同時寫同一筆資料,最後誰為準?
- Binlog 延遲 / 迴圈複寫:設定不慎會出現奇怪的循環與延遲。
- 自動故障切換缺乏共識機制:容易出現 Split Brain,兩邊都以為自己是主。
因此,雙主複寫通常只在「流量不大、操作者極熟悉風險」的場景使用,並不適合作為泛用的 Active-Active 架構。
2. MariaDB Galera / Percona XtraDB Cluster — ✅ 最常見的 Active-Active 方案之一
Galera Cluster 是一種 同步多主複寫 的技術,MariaDB / Percona 等都提供整合套件。 在 Galera 中,所有節點共享同一個「寫入視圖」,透過一致性演算法,確保所有節點看見的寫入順序相同。
特性:
- 每個節點都可以讀寫(多主)。
- 透過
wsrep_*參數控制複寫行為與 Flow Control。 - 支援 同步或者「準同步」 的寫入確認機制。
優點:
- 原生支援多主寫入,設計就是為 Active-Active 而生。
- 內建 Quorum / 仲裁,較容易避免腦裂。
- 有完整的社群實務與文件,搭配 HAProxy / ProxySQL 成熟度高。
3. MySQL Group Replication / InnoDB Cluster — ✅ 官方多主方案
MySQL 官方則提供 Group Replication 及其上層封裝 InnoDB Cluster, 一樣提供 多主寫入 + 共識協議 + 自動故障處理 的能力。
特性:
- 支援 Single-Primary 以及 Multi-Primary 模式。
- 搭配 MySQL Router 可提供智慧路由(讀寫分離、故障節點剔除)。
- 與 MySQL Shell 整合,建立與維運相對直覺。
如果你偏好「全部用官方元件」,InnoDB Cluster + Router 會是很好的選擇。
4. Proxy 層(ProxySQL / HAProxy)+ VIP(Keepalived)— Active-Active 的「入口大腦」
無論是 Galera 或 InnoDB Cluster,要讓外部應用程式「簡單連一個位址就好」,幾乎都會搭配:
- Proxy 層:ProxySQL 或 HAProxy,負責:
- 健康檢查(Health Check)。
- 把流量分配到健康節點。
- 讀寫分離(看架構需求)。
- 虛擬 IP(VIP)或 DNS: 透過 Keepalived / LVS / DNS(短 TTL)讓應用端只記住單一連線位址。這也是故障切換的入口。
🧠 三、設計 Active-Active 架構前,必須先想清楚的幾件事
- 一致性模型: 你的系統是偏向「強一致」還是「可接受短暫不一致」?
- 寫入衝突: 有沒有大量跨節點同時更新同一筆資料的情況?
- 故障切換策略: 誰來決定「哪台下線」?是 ProxySQL / HAProxy / Router / Orchestrator 還是人手?
- 維運能力: 團隊是否熟悉這些元件的排錯?(Flow Control、GTID、仲裁、Split Brain…)
以下我們用兩個常見實作範例,帶你從零組出一套可以上線的架構。
🏗️ 四、範例架構一:MariaDB Galera + HAProxy + Keepalived(常見企業組合)
1. 架構拓樸示意
┌─────────────────────────────┐
│ 應用程式層(Web / API) │
└─────────────▲──────────────┘
│ 連線(單一 VIP)
VIP: 10.0.0.100:3306
│
┌──────────────┴──────────────┐
│ │
HAProxy + Keepalived A HAProxy + Keepalived B
10.0.0.11 10.0.0.12
│ │
┌─────────┴────────┐ ┌────────┴─────────┐
│ │ │ │
DB1 (Galera) DB2 (Galera) DB3 (Galera) (可選 Arbitrator)
設計重點:
- 3 個 Galera 節點維持 Quorum,避免雙節點腦裂。
- 2 台 HAProxy + Keepalived 組成前端雙機,對外只暴露 VIP。
- 應用程式記住的只有
10.0.0.100:3306,背後切換完全透明。
2. Galera 基本安裝與設定(示意)
以下以 MariaDB + Galera 為例,每台 DB 節點的 /etc/my.cnf.d/galera.cnf 可能包含:
[galera]
wsrep_on = ON
wsrep_provider = /usr/lib64/galera/libgalera_smm.so
# 集群名稱
wsrep_cluster_name = 'prod-galera-cluster'
# 集群成員清單(所有節點一致)
wsrep_cluster_address = 'gcomm://10.0.0.21,10.0.0.22,10.0.0.23'
# 本節點位址(每台不同)
wsrep_node_address = '10.0.0.21'
wsrep_node_name = 'db1'
# 複寫設定
binlog_format = ROW
default_storage_engine = InnoDB
innodb_autoinc_lock_mode = 2
# 流量控制(依實際情況調整)
wsrep_slave_threads = 8
wsrep_certification_rules= STRICT
初始化步驟(概念示意):
- 在第一台 DB(db1)使用
galera_new_cluster/ bootstrap 方式啟動集群。 - 在 db2、db3 直接啟動 MariaDB,會透過
wsrep_cluster_address找到集群並自動同步。 - 確認
SHOW STATUS LIKE 'wsrep_cluster_size';顯示節點數量正確。
3. HAProxy 與健康檢查設定
在 HAProxy 節點中,可設定一組後端指向 Galera 節點,並透過 mysql-check 檢查健康狀態:
frontend mysql_front
bind *:3306
mode tcp
default_backend galera_back
backend galera_back
mode tcp
balance roundrobin
option mysql-check user haproxy_check
server db1 10.0.0.21:3306 check
server db2 10.0.0.22:3306 check
server db3 10.0.0.23:3306 check backup
實務上可以依需求把某些節點標為 backup,或進一步利用 ProxySQL 做「寫入集中 / 讀取分散」,
例如:一台節點主負責寫入,其餘放讀取為主。
4. Keepalived 虛擬 IP 設定
以 A 節點為主、B 節點為備的 Keepalived 設定示意:
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass ha-vip-pass
}
virtual_ipaddress {
10.0.0.100/24 dev eth0
}
}
在 B 節點則將 state 設為 BACKUP,priority 設低一些,例如 100。當 A 節點失效時,VIP 會飄到 B 節點。
5. 故障情境簡單驗證
- 關閉其中一個 DB 節點:確認 HAProxy 檢測到後,從後端池中移除該節點。
- 關閉 A 節點整機:確認 VIP 飄移到 B 節點,應用程式連線不中斷(或短暫中斷後恢復)。
- 刻意製造網路隔離:確認 Galera 的 Quorum 行為,避免雙邊都對外提供服務。
🏗️ 五、範例架構二:MySQL InnoDB Cluster + MySQL Router + Keepalived
若偏好 MySQL 官方技術堆疊,可以改用:
- InnoDB Cluster:底層使用 Group Replication 完成多主複寫。
- MySQL Router:提供智慧路由端點(讀 / 寫不同埠、故障節點剔除)。
1. 建立 InnoDB Cluster(示意流程)
透過 MySQL Shell 建立集群的概念示意:
# 在 mysqlsh 中
dba.createCluster("prodCluster", {multiPrimary: true})
# 加入節點
cluster = dba.getCluster()
cluster.addInstance("root@10.0.0.31:3306")
cluster.addInstance("root@10.0.0.32:3306")
cluster.addInstance("root@10.0.0.33:3306")
# 查看狀態
cluster.status()
設定 multiPrimary: true 後,集群中的節點都可以寫入,InnoDB Cluster 會處理寫入排序與共識。
2. 部署 MySQL Router
在 Router 節點上,使用 MySQL Shell 為 Router 帶出設定:
# 產出 Router 設定
dba.configureRouterInstance("router@10.0.0.41:6446", {
clusterName: "prodCluster"
})
Router 啟動後會暴露類似:
6446:讀寫端點。6447:唯讀端點。
再搭配 Keepalived 將 Router 節點做成雙機,對外一樣只提供一個 VIP。
⏱ 六、自動故障偵測與 Failover 流程設計
Active-Active 架構的重點,不只是「多台都在跑」,而是某台掛了之後能不能自動切換且不寫壞資料。
一般會拆成三個層次:
- 資料庫層: Galera / Group Replication 透過投票機制決定誰還在集群內。
- Proxy / Router 層: HAProxy / ProxySQL / MySQL Router 透過健康檢查,把「沒通」或「不在集群內」的節點踢掉。
- VIP / DNS 層: Keepalived 或短 TTL DNS 控制「入口在哪一台 Proxy」。
設計原則:
- 任一層都不要單點(Single Point of Failure)。
- 「誰有權決定把節點踢出服務」一定要清楚(避免多頭決策)。
- 故障偵測要有 grace period,避免短暫網路抖動帶來過度切換。
🩹 七、常見陷阱與排錯清單
- 腦裂(Split Brain): 網路分割後,兩邊都以為自己是主,結果同時接受寫入。
- Flow Control 造成延遲: Galera / Group Replication 中,落後太多的節點可能拖慢整體提交速度。
- 不當讀寫分配: 把「強一致要求」的寫入分散到多個節點,造成大量鎖競爭。
- 健康檢查過於單純: 只檢查 3306 活著就當正常,沒有檢查「是否仍在集群內」。
- 備份與快照不一致: 沒有意識到多主架構下備份點的選擇與一致性問題。
建議排錯方向:
- 先看 DB 層集群狀態(
wsrep_cluster_status、cluster.status())。 - 再看 Proxy 層後端池狀態(
show servers state/ HAProxy stats)。 - 最後看 VIP / DNS 的指向與 ARP 解析是否正確。
💾 八、備份、維護與 Schema 變更策略
Active-Active 架構下,備份與維護要特別小心:
- 備份: 建議統一由「指定節點」執行,並確認該節點在備份期間不被 Proxy 當成主要寫入節點。
- Schema 變更: 儘量減少大規模 ALTER,改用 Online DDL 或分批變更。
- 維護窗口: 建議配合負載低谷,且事先模擬「節點下線 / 重新加入」流程。
如果你已經有 PostgreSQL 50 萬筆資料實驗 那類基準環境,可以比照方式為 Active-Active 架構建立壓測腳本,測試:
- 單節點故障時的吞吐穩定度。
- 節點重新加入時帶來的同步負載。
- 在大量 DML / DDL 操作下,Flow Control 的影響程度。
🎯 九、什麼情境適合 Active-Active?什麼情境不適合?
適合採用 Active-Active 的情境:
- 讀寫流量都偏高,需要同時提升 吞吐與可用性。
- 服務 不能接受長時間的主備切換窗口,例如金融交易、即時訂單系統。
- 團隊有一定的 DBA / SRE 能量,願意投資在監控與排錯流程上。
暫時不建議採用 Active-Active 的情境:
- 資料模型高度集中於單一幾張表,每次交易都會修改同一批 row,鎖競爭極嚴重。
- 團隊缺乏 DBA 經驗,以往連單主複寫都常出包。
- 系統規模尚小,一台主機 + 一台備援就足以支撐流量。
這時候,反而建議先把基礎打好,例如:
- 設計合理的 Active-Passive + 自動 Failover。
- 先做 讀寫分離,把讀流量切到副本上。
- 再逐步演進到多主架構。
🧭 十、實作行動清單(可當專案 Checklist)
✅ 明確定義系統一致性需求(強一致 / 最終一致)
✅ 選定多主複寫方案(Galera / InnoDB Cluster / 其他)
✅ 規劃節點數量:至少 3 DB 節點或 2 DB + 1 Arbitrator
✅ 部署 Proxy 層(HAProxy / ProxySQL / MySQL Router)
✅ 部署 VIP / DNS 切換機制(Keepalived / 短 TTL DNS)
✅ 建立故障情境測試腳本(節點當機、網路中斷、重啟)
✅ 建立備份與還原驗證流程(定期演練)
✅ 針對 Flow Control / Quorum / Split Brain 建立 SOP
✅ 將所有步驟文件化,納入維運 Runbook
💬 互動留言引導(Active-Active 實戰分享區)
如果你在實作 MySQL / MariaDB Active-Active 高可用架構 的過程中:
- 遇到 Flow Control、延遲或鎖競爭問題
- 對 Galera / InnoDB Cluster / ProxySQL 設計有疑問
- 或想分享你實際踩到的坑、架構拓樸
歡迎直接在下方留言 👇 簡單描述你的 節點數量、版本、架構圖,我會把常見問題整理成後續補充篇或 FAQ 更新在本文。
🔗 延伸閱讀
- 🛠 Linux RAID 完整指南:RAID0/1/5/10 實作與維護全攻略
- 🌐 Proxmox VE 企業部署藍圖:叢集、Ceph、ZFS、備份與網路全方位指南
- 🛡 Linux Fail2Ban 進階實戰:SSH / Web 防暴力破解完整設定
- 📊 PostgreSQL 大型資料庫實作:50 萬筆資料建立、查詢與備份還原效能實驗
- 🗄️ MariaDB Galera Cluster Active-Active 高可用架構:三節點集群 + Proxy 實作全流程
📘 結語:從「兩台 DB」到「一整個服務系統」的思維
很多人在談 Active-Active 架構時,直覺想到的是「把兩台資料庫做成主主複寫」; 但當你實際部署過 Galera、InnoDB Cluster、ProxySQL、HAProxy、Keepalived 等組合之後, 會發現它真正改變的是:你開始用「整個服務系統」而不是「單一資料庫」的角度在設計架構。
如果你目前正在規劃資料庫高可用升級、或者正要從 Active-Passive 過渡到 Active-Active, 很推薦把本文當成 專案 Checklist 與學習地圖, 搭配上面延伸閱讀中的 Proxmox、Linux 安全與 PostgreSQL 實驗系列,一步一步把整體基礎打穩。
— WWFandy・MySQL / MariaDB HA 筆記
沒有留言: