Group Replication 架構概述
MySQL Group Replication 是 MySQL 5.7.17 版本開始引入的高可用性解決方案,它提供了一個分散式、容錯的資料庫叢集架構。Group Replication 基於 Paxos 協議的變體實現,確保叢集中的所有節點在任何時刻都擁有一致的資料視圖。
核心特性
- 虛擬同步複製:確保交易在提交前已被多數節點確認
- 自動故障偵測:透過心跳機制自動偵測節點故障
- 自動成員管理:節點可以自動加入或離開叢集
- 衝突偵測與解決:自動處理並發交易衝突
- 分散式恢復:新節點可從現有成員自動同步資料
架構圖示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| ┌─────────────────────────────────────────────────────────────┐
│ MySQL Group Replication │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Node 1 │◄────►│ Node 2 │◄────►│ Node 3 │ │
│ │(Primary)│ │(Secondary) │(Secondary) │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ └───────────────┼────────────────┘ │
│ │ │
│ Group Communication System (GCS) │
│ (Paxos Protocol) │
│ │
└─────────────────────────────────────────────────────────────┘
|
成員數量建議
Group Replication 需要奇數個節點以確保正確的仲裁機制:
| 節點數量 | 容錯能力 | 建議使用場景 |
|---|
| 3 節點 | 1 節點故障 | 小型生產環境 |
| 5 節點 | 2 節點故障 | 中型生產環境 |
| 7 節點 | 3 節點故障 | 大型關鍵任務 |
單主模式與多主模式比較
MySQL Group Replication 提供兩種運作模式:單主模式(Single-Primary Mode)和多主模式(Multi-Primary Mode)。
單主模式(Single-Primary Mode)
在單主模式下,叢集中只有一個節點接受寫入操作,其他節點為唯讀副本。
優點:
- 避免寫入衝突
- 資料一致性較容易維護
- 適合傳統應用程式架構
- 效能較為穩定
缺點:
- 寫入能力無法水平擴展
- Primary 節點為單點瓶頸
1
2
3
| -- 查詢目前的主節點
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;
|
多主模式(Multi-Primary Mode)
在多主模式下,所有節點都可以接受讀寫操作。
優點:
缺點:
- 可能產生寫入衝突
- 需要應用程式層級的衝突處理策略
- 不支援序列化隔離等級
1
2
3
| -- 啟用多主模式的設定
SET GLOBAL group_replication_single_primary_mode = OFF;
SET GLOBAL group_replication_enforce_update_everywhere_checks = ON;
|
模式選擇建議
| 使用情境 | 建議模式 | 原因 |
|---|
| 傳統 OLTP 應用 | 單主模式 | 避免衝突,簡化設計 |
| 讀寫分離架構 | 單主模式 | 明確的主從角色 |
| 微服務架構 | 多主模式 | 分散寫入負載 |
| 地理分散部署 | 多主模式 | 降低寫入延遲 |
節點環境準備
以下步驟將在三台 Ubuntu 22.04 伺服器上建立 MySQL Group Replication 叢集。
伺服器規劃
1
2
3
| 節點1 (Primary) : 192.168.1.101 mysql-node1
節點2 (Secondary) : 192.168.1.102 mysql-node2
節點3 (Secondary) : 192.168.1.103 mysql-node3
|
步驟一:更新系統並設定主機名稱
在每台伺服器上執行:
1
2
3
4
5
| # 更新系統套件
sudo apt update && sudo apt upgrade -y
# 設定主機名稱(依據節點調整)
sudo hostnamectl set-hostname mysql-node1
|
編輯 /etc/hosts 檔案,加入所有節點的對應:
1
2
3
4
5
| sudo tee -a /etc/hosts << EOF
192.168.1.101 mysql-node1
192.168.1.102 mysql-node2
192.168.1.103 mysql-node3
EOF
|
步驟二:安裝 MySQL 8.0
1
2
3
4
5
| # 安裝 MySQL Server
sudo apt install mysql-server -y
# 確認 MySQL 版本
mysql --version
|
步驟三:設定防火牆
MySQL Group Replication 需要開放以下連接埠:
1
2
3
4
5
6
7
8
| # 開放 MySQL 連接埠
sudo ufw allow 3306/tcp
# 開放 Group Replication 通訊連接埠
sudo ufw allow 33061/tcp
# 確認防火牆規則
sudo ufw status
|
步驟四:設定 MySQL 組態
編輯 /etc/mysql/mysql.conf.d/mysqld.cnf:
節點 1 設定:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| [mysqld]
# 基本設定
server-id = 1
bind-address = 0.0.0.0
log_bin = mysql-bin
binlog_format = ROW
binlog_checksum = NONE
gtid_mode = ON
enforce_gtid_consistency = ON
log_slave_updates = ON
# Group Replication 設定
plugin_load_add = 'group_replication.so'
group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot = OFF
group_replication_local_address = "192.168.1.101:33061"
group_replication_group_seeds = "192.168.1.101:33061,192.168.1.102:33061,192.168.1.103:33061"
group_replication_bootstrap_group = OFF
# 單主模式(預設)
group_replication_single_primary_mode = ON
group_replication_enforce_update_everywhere_checks = OFF
# 效能調校
transaction_write_set_extraction = XXHASH64
slave_parallel_workers = 4
slave_preserve_commit_order = ON
slave_parallel_type = LOGICAL_CLOCK
|
節點 2 設定(僅需修改以下項目):
1
2
| server-id = 2
group_replication_local_address = "192.168.1.102:33061"
|
節點 3 設定(僅需修改以下項目):
1
2
| server-id = 3
group_replication_local_address = "192.168.1.103:33061"
|
步驟五:重啟 MySQL 服務
1
2
| sudo systemctl restart mysql
sudo systemctl status mysql
|
步驟六:建立複製使用者
在每個節點上建立用於 Group Replication 的使用者:
1
2
3
4
5
6
7
8
9
10
11
12
| -- 登入 MySQL
sudo mysql -u root
-- 建立複製使用者
CREATE USER 'repl'@'%' IDENTIFIED BY 'StrongPassword123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
GRANT BACKUP_ADMIN ON *.* TO 'repl'@'%';
GRANT GROUP_REPLICATION_STREAM ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
-- 設定 Group Replication 恢復通道的認證資訊
CHANGE REPLICATION SOURCE TO SOURCE_USER='repl', SOURCE_PASSWORD='StrongPassword123!' FOR CHANNEL 'group_replication_recovery';
|
叢集初始化與成員加入
初始化第一個節點(Bootstrap)
在節點 1 上執行以下步驟來啟動叢集:
1
2
3
4
5
6
7
8
9
10
| -- 確認 Group Replication 外掛已載入
SHOW PLUGINS LIKE 'group_replication';
-- 啟動叢集引導程序
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;
-- 確認節點狀態
SELECT * FROM performance_schema.replication_group_members;
|
預期輸出:
1
2
3
4
5
| +---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+
| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+
| group_replication_applier | xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | mysql-node1 | 3306 | ONLINE | PRIMARY |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+
|
加入第二個節點
在節點 2 上執行:
1
2
3
4
5
| -- 啟動 Group Replication
START GROUP_REPLICATION;
-- 確認節點狀態
SELECT * FROM performance_schema.replication_group_members;
|
加入第三個節點
在節點 3 上執行相同步驟:
1
2
| START GROUP_REPLICATION;
SELECT * FROM performance_schema.replication_group_members;
|
驗證叢集狀態
所有節點加入後,執行以下查詢確認叢集健康狀態:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| -- 查看所有成員狀態
SELECT
MEMBER_HOST,
MEMBER_PORT,
MEMBER_STATE,
MEMBER_ROLE,
MEMBER_VERSION
FROM performance_schema.replication_group_members;
-- 查看複製延遲
SELECT
CHANNEL_NAME,
RECEIVED_TRANSACTION_SET,
LAST_APPLIED_TRANSACTION
FROM performance_schema.replication_connection_status;
-- 檢查 GTID 執行狀態
SHOW GLOBAL VARIABLES LIKE 'gtid_executed';
|
測試資料複製
在 Primary 節點建立測試資料:
1
2
3
4
5
6
7
8
9
| -- 在 Primary 節點執行
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE test_table (
id INT AUTO_INCREMENT PRIMARY KEY,
data VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO test_table (data) VALUES ('Hello Group Replication');
|
在 Secondary 節點驗證:
1
2
3
| -- 在任一 Secondary 節點執行
USE testdb;
SELECT * FROM test_table;
|
故障切換與自動恢復
自動故障偵測機制
MySQL Group Replication 使用基於心跳的故障偵測機制:
1
2
3
4
5
| -- 查看心跳間隔設定(預設 5 秒)
SHOW GLOBAL VARIABLES LIKE 'group_replication_member_expel_timeout';
-- 調整成員驅逐超時(單位:秒)
SET GLOBAL group_replication_member_expel_timeout = 5;
|
模擬主節點故障
1
2
| # 在節點 1 上停止 MySQL 服務
sudo systemctl stop mysql
|
在其他節點觀察故障切換:
1
2
3
| -- 觀察成員狀態變化
SELECT MEMBER_HOST, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;
|
輸出示例(Primary 已自動切換):
1
2
3
4
5
6
| +-------------+--------------+--------------+
| MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+-------------+--------------+--------------+
| mysql-node2 | ONLINE | PRIMARY |
| mysql-node3 | ONLINE | SECONDARY |
+-------------+--------------+--------------+
|
節點自動恢復
當故障節點恢復後,可自動重新加入叢集:
1
2
| # 啟動 MySQL 服務
sudo systemctl start mysql
|
1
2
3
4
5
| -- 重新啟動 Group Replication
START GROUP_REPLICATION;
-- 確認節點已重新加入
SELECT * FROM performance_schema.replication_group_members;
|
手動故障切換
在單主模式下,可以手動指定新的 Primary 節點:
1
2
3
4
5
6
7
| -- 查看目前的 Primary
SELECT MEMBER_HOST, MEMBER_ROLE
FROM performance_schema.replication_group_members
WHERE MEMBER_ROLE = 'PRIMARY';
-- 手動切換 Primary(使用目標節點的 MEMBER_ID)
SELECT group_replication_set_as_primary('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
設定自動啟動
為確保系統重啟後 Group Replication 自動啟動:
1
2
| # 在 mysqld.cnf 中加入
group_replication_start_on_boot = ON
|
建議搭配延遲啟動以確保網路就緒:
1
2
3
4
5
6
7
| # 建立 systemd override
sudo mkdir -p /etc/systemd/system/mysql.service.d
sudo tee /etc/systemd/system/mysql.service.d/override.conf << EOF
[Service]
ExecStartPre=/bin/sleep 10
EOF
sudo systemctl daemon-reload
|
衝突解決機制
衝突類型
在多主模式下,可能發生以下類型的衝突:
- 寫入衝突:多個節點同時修改相同的資料列
- 認證衝突:交易在認證階段被拒絕
- DDL 衝突:結構變更操作的衝突
衝突偵測原理
Group Replication 使用「先提交者勝出」(First Committer Wins)策略:
1
2
3
4
5
6
7
8
9
| 交易 T1 (Node1) 交易 T2 (Node2)
| |
BEGIN BEGIN
| |
UPDATE row X UPDATE row X
| |
COMMIT COMMIT
| |
(成功) (失敗 - 回滾)
|
監控衝突狀況
1
2
3
4
5
6
7
8
9
10
| -- 查看衝突統計
SELECT
COUNT_TRANSACTIONS_CHECKED,
COUNT_CONFLICTS_DETECTED,
COUNT_TRANSACTIONS_ROWS_VALIDATING
FROM performance_schema.replication_group_member_stats
WHERE MEMBER_ID = @@server_uuid;
-- 查看認證資訊
SELECT * FROM performance_schema.replication_group_member_stats\G
|
降低衝突的最佳實踐
- 適當的表格設計
1
2
3
4
5
6
7
| -- 使用 UUID 作為主鍵,避免自動遞增衝突
CREATE TABLE orders (
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
customer_id INT,
order_date DATETIME,
total_amount DECIMAL(10,2)
);
|
- 應用層衝突處理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import mysql.connector
from mysql.connector import Error
def execute_with_retry(connection, query, max_retries=3):
for attempt in range(max_retries):
try:
cursor = connection.cursor()
cursor.execute(query)
connection.commit()
return True
except Error as e:
if e.errno == 3101: # ER_GR_CERTIFICATION_ERROR
print(f"Certification conflict, retry {attempt + 1}")
connection.rollback()
continue
raise
return False
|
- 分區寫入策略
1
2
3
4
| -- 按照資料範圍分配寫入節點
-- Node1 處理 customer_id 1-1000
-- Node2 處理 customer_id 1001-2000
-- Node3 處理 customer_id 2001-3000
|
監控與管理
MySQL Group Replication 提供豐富的 Performance Schema 表格:
1
2
3
4
5
6
7
8
9
10
11
| -- 成員資訊
SELECT * FROM performance_schema.replication_group_members;
-- 成員統計
SELECT * FROM performance_schema.replication_group_member_stats;
-- 連線狀態
SELECT * FROM performance_schema.replication_connection_status;
-- 應用程式狀態
SELECT * FROM performance_schema.replication_applier_status;
|
建立監控腳本
建立 /opt/mysql-gr-monitor.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| #!/bin/bash
MYSQL_USER="monitor"
MYSQL_PASSWORD="MonitorPassword123!"
LOG_FILE="/var/log/mysql-gr-monitor.log"
check_gr_status() {
status=$(mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -N -e "
SELECT COUNT(*)
FROM performance_schema.replication_group_members
WHERE MEMBER_STATE = 'ONLINE';
")
echo "$(date '+%Y-%m-%d %H:%M:%S') - Online members: ${status}" >> ${LOG_FILE}
if [ ${status} -lt 2 ]; then
echo "WARNING: Less than 2 online members!" | mail -s "GR Alert" admin@example.com
fi
}
check_replication_lag() {
lag=$(mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -N -e "
SELECT
COALESCE(
TIMESTAMPDIFF(SECOND,
LAST_APPLIED_TRANSACTION_END_APPLY_TIMESTAMP,
NOW()
), 0
) as lag
FROM performance_schema.replication_applier_status_by_worker
WHERE CHANNEL_NAME = 'group_replication_applier'
ORDER BY lag DESC
LIMIT 1;
")
echo "$(date '+%Y-%m-%d %H:%M:%S') - Max replication lag: ${lag}s" >> ${LOG_FILE}
if [ ${lag} -gt 10 ]; then
echo "WARNING: Replication lag exceeds 10 seconds!" | mail -s "GR Lag Alert" admin@example.com
fi
}
check_gr_status
check_replication_lag
|
設定定期執行:
1
2
| # 加入 crontab
*/5 * * * * /opt/mysql-gr-monitor.sh
|
使用 Prometheus 和 Grafana 監控
安裝 MySQL Exporter:
1
2
3
4
| # 下載 mysqld_exporter
wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.15.0/mysqld_exporter-0.15.0.linux-amd64.tar.gz
tar xvf mysqld_exporter-0.15.0.linux-amd64.tar.gz
sudo mv mysqld_exporter-0.15.0.linux-amd64/mysqld_exporter /usr/local/bin/
|
建立監控使用者:
1
2
3
| CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'ExporterPassword123!';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';
FLUSH PRIVILEGES;
|
建立 systemd 服務:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| sudo tee /etc/systemd/system/mysqld_exporter.service << EOF
[Unit]
Description=MySQL Exporter
After=network.target
[Service]
Type=simple
User=mysql
Environment="DATA_SOURCE_NAME=exporter:ExporterPassword123!@(localhost:3306)/"
ExecStart=/usr/local/bin/mysqld_exporter --collect.global_status --collect.info_schema.processlist --collect.slave_status
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable mysqld_exporter
sudo systemctl start mysqld_exporter
|
重要監控指標
| 指標 | 說明 | 警告閾值 |
|---|
mysql_perf_schema_replication_group_members | 線上成員數 | < (N/2 + 1) |
mysql_perf_schema_replication_group_member_stats_count_conflicts_detected | 衝突數量 | 持續增加 |
mysql_slave_lag_seconds | 複製延遲 | > 10 秒 |
mysql_global_status_threads_running | 運行中執行緒 | > 100 |
與 InnoDB Cluster 整合
InnoDB Cluster 架構
InnoDB Cluster 是 MySQL 官方的高可用性解決方案,它整合了以下元件:
- MySQL Group Replication:提供資料同步和容錯
- MySQL Router:智慧路由和負載平衡
- MySQL Shell:叢集管理和配置工具
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| ┌─────────────────────────────────────────────────────────────────────┐
│ InnoDB Cluster │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ 應用程式 │ │
│ └──────┬──────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ MySQL Router │──────────────────────────────────┐ │
│ └──────────────┘ │ │
│ │ │ │
│ ┌─────┴─────┬─────────────┐ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌──────────┐ │
│ │Node1│ │Node2│ │Node3│ │MySQL Shell│ │
│ │(R/W)│ │(R/O)│ │(R/O)│ │(管理工具) │ │
│ └─────┘ └─────┘ └─────┘ └──────────┘ │
│ └─────────┴─────────────┘ │
│ Group Replication │
│ │
└─────────────────────────────────────────────────────────────────────┘
|
安裝 MySQL Shell
1
2
3
4
5
| # 下載並安裝 MySQL Shell
wget https://dev.mysql.com/get/mysql-apt-config_0.8.28-1_all.deb
sudo dpkg -i mysql-apt-config_0.8.28-1_all.deb
sudo apt update
sudo apt install mysql-shell -y
|
使用 MySQL Shell 配置 InnoDB Cluster
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // 連接到第一個節點
mysqlsh --uri root@192.168.1.101
// 檢查節點配置
dba.checkInstanceConfiguration('root@192.168.1.101')
// 配置節點以支援 InnoDB Cluster
dba.configureInstance('root@192.168.1.101')
// 建立叢集
var cluster = dba.createCluster('myCluster')
// 加入其他節點
cluster.addInstance('root@192.168.1.102')
cluster.addInstance('root@192.168.1.103')
// 查看叢集狀態
cluster.status()
|
安裝與配置 MySQL Router
1
2
3
4
5
6
7
8
9
| # 安裝 MySQL Router
sudo apt install mysql-router -y
# 引導 Router 配置
sudo mysqlrouter --bootstrap root@192.168.1.101:3306 --directory /etc/mysqlrouter --user=mysqlrouter
# 啟動 MySQL Router
sudo systemctl start mysqlrouter
sudo systemctl enable mysqlrouter
|
Router 會自動配置以下連接埠:
| 連接埠 | 用途 | 模式 |
|---|
| 6446 | 讀寫連線 | 連接 Primary |
| 6447 | 唯讀連線 | 負載平衡 Secondary |
| 6448 | 讀寫連線 (X Protocol) | 連接 Primary |
| 6449 | 唯讀連線 (X Protocol) | 負載平衡 Secondary |
應用程式連線配置
使用 MySQL Router 的連線字串:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| import mysql.connector
# 讀寫連線(自動連接 Primary)
write_config = {
'host': 'localhost',
'port': 6446,
'user': 'app_user',
'password': 'AppPassword123!',
'database': 'mydb'
}
# 唯讀連線(負載平衡到 Secondary)
read_config = {
'host': 'localhost',
'port': 6447,
'user': 'app_user',
'password': 'AppPassword123!',
'database': 'mydb'
}
# 寫入操作
write_conn = mysql.connector.connect(**write_config)
write_cursor = write_conn.cursor()
write_cursor.execute("INSERT INTO users (name) VALUES ('John')")
write_conn.commit()
# 讀取操作
read_conn = mysql.connector.connect(**read_config)
read_cursor = read_conn.cursor()
read_cursor.execute("SELECT * FROM users")
results = read_cursor.fetchall()
|
InnoDB Cluster 管理命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // 連接到叢集
mysqlsh --uri root@192.168.1.101
var cluster = dba.getCluster()
// 查看詳細狀態
cluster.status({extended: 1})
// 設定選項
cluster.setOption('expelTimeout', 5)
// 重新掃描叢集
cluster.rescan()
// 移除節點
cluster.removeInstance('root@192.168.1.103')
// 強制仲裁(在多數節點故障時使用)
cluster.forceQuorumUsingPartitionOf('root@192.168.1.101')
// 解散叢集
cluster.dissolve()
|
備份與還原
InnoDB Cluster 支援使用 MySQL Enterprise Backup 或 mysqlpump:
1
2
3
4
5
6
| # 使用 mysqlpump 備份
mysqlpump -u root -p --all-databases --add-drop-database > cluster_backup.sql
# 使用 xtrabackup 進行線上備份
xtrabackup --backup --target-dir=/backup/full --user=root --password=xxx
xtrabackup --prepare --target-dir=/backup/full
|
總結
MySQL Group Replication 提供了強大的高可用性解決方案,特別適合需要自動故障切換和資料一致性保證的生產環境。透過本文的指南,您應該能夠:
- 理解 Group Replication 的架構和運作原理
- 在 Ubuntu 22.04 上部署三節點叢集
- 正確處理故障切換和節點恢復
- 監控叢集健康狀態
- 整合 InnoDB Cluster 實現完整的高可用性架構
進階學習資源