高可用架構#

MySQL 的高可用架構是生產環境的核心保障。理解主備複製原理和切換策略,是構建可靠資料庫系統的基礎。

主備複製原理#

基本架構#

sequenceDiagram
    participant Client as 客戶端
    participant M as 主庫 A
    participant S as 備庫 B

    Client->>M: 客戶端請求
    M->>M: 執行 SQL
    M->>M: 寫 binlog

    M->>S: 傳輸 binlog (io_thread)
    S->>S: 寫入 relay log
    S->>S: 執行 relay log (sql_thread)

複製流程詳解#

  1. 備庫連接主庫:執行 CHANGE MASTER 設置主庫信息
  2. 啟動複製線程START SLAVE 啟動 io_thread 和 sql_thread
  3. 主庫發送日誌:主庫讀取 binlog 發送給備庫
  4. 備庫接收存儲:io_thread 寫入中轉日誌(relay log)
  5. 備庫執行日誌:sql_thread 解析執行 relay log

雙 M 結構#

flowchart LR
    subgraph MM["雙 M 結構"]
        A["節點 A"] <-->|"互為主備<br/>binlog 雙向同步"| B["節點 A'"]
    end

    style A fill:#bbdefb
    style B fill:#c8e6c9
狀態節點 A節點 A'
正常時主庫備庫
切換後備庫主庫

雙 M 結構的優勢是切換時不需要修改主備關係,只需切換業務流量。

循環複製問題#

雙 M 結構中,為避免循環複製,MySQL 使用 server_id 機制:

  1. 兩個庫的 server_id 必須不同
  2. 備庫重放日誌時,生成的 binlog 保留原 server_id
  3. 收到 server_id 與自己相同的日誌時,直接丟棄

binlog 格式選擇#

三種格式對比#

格式內容優點缺點
statementSQL 原文日誌小可能主從不一致
row行資料變更資料一致日誌較大
mixed自動選擇折中方案已較少使用

為什麼推薦 row 格式#

-- statement 格式的風險
DELETE FROM t WHERE a >= 4 AND t_modified <= '2018-11-10' LIMIT 1;
-- 主備可能使用不同索引,刪除不同的行!

-- row 格式記錄被刪除行的主鍵
-- 備庫一定會刪除相同的行

row 格式的額外好處是誤操作恢復:DELETE 誤刪可從 binlog 轉成 INSERT;INSERT 誤插可從 binlog 轉成 DELETE;UPDATE 誤改可交換前後資料重新執行。

主備延遲#

延遲的定義#

主備延遲 = T3 - T1

T1: 主庫執行完事務的時間
T2: 備庫接收完 binlog 的時間
T3: 備庫執行完事務的時間

查看延遲:

SHOW SLAVE STATUS;
-- 關注 seconds_behind_master 字段

延遲的來源#

原因說明解決方案
備庫機器差備庫 IO/CPU 不足主備對等部署
備庫壓力大備庫承載查詢一主多從分擔讀
大事務單事務執行時間長拆分大事務
大表 DDL表結構變更耗時使用 gh-ost
並行複製不足單線程執行啟用並行複製

大事務是延遲的主要原因之一。主庫事務執行 10 分鐘,備庫就會延遲 10 分鐘。

並行複製#

演進歷程#

MySQL 5.5: 單線程複製
    ↓
MySQL 5.6: 按庫並行
    ↓
MySQL 5.7: 基於 LOGICAL_CLOCK
    ↓
MySQL 5.7.22: WRITESET 策略

並行複製策略#

MySQL 5.6 - 按庫並行

  • 不同庫的事務可以並行執行
  • 限制:所有表在同一庫則無效

MySQL 5.7 - LOGICAL_CLOCK

  • 同時處於 prepare 狀態的事務可以並行
  • 通過 binlog_group_commit_sync_delay 增加並行度

MySQL 5.7.22 - WRITESET

  • 沒有修改相同行的事務可以並行
  • 最優的並行度
-- 設置並行複製策略
SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK';
SET GLOBAL slave_parallel_workers = 16;

-- MySQL 5.7.22+
SET GLOBAL binlog_transaction_dependency_tracking = 'WRITESET';

主備切換策略#

可靠性優先策略#

1. 等待 seconds_behind_master < 5s
2. 主庫設置 readonly = true
3. 等待 seconds_behind_master = 0
4. 備庫設置 readonly = false
5. 業務切換到備庫

特點

  • 有短暫不可用時間
  • 保證資料一致性

可用性優先策略#

1. 備庫設置 readonly = false
2. 業務切換到備庫
3. 等待同步完成

特點

  • 幾乎無不可用時間
  • 可能資料不一致

推薦使用可靠性優先策略。對於資料服務,資料可靠性通常比可用性更重要。

異常切換#

主庫故障時,無法等待延遲歸零:

延遲時間影響
很小快速切換,影響小
較大要麼等待(不可用),要麼立即切換(可能丟資料)

減少主備延遲是提高可用性的關鍵。延遲越小,切換越快,可用性越高。

GTID 模式#

什麼是 GTID#

GTID(Global Transaction Identifier)= server_uuid:gno

  • server_uuid:實例的唯一標識
  • gno:事務序號,每次提交遞增

GTID 的優勢#

傳統位點方式的問題

CHANGE MASTER TO
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=154;
-- 需要手動計算位點,容易出錯

GTID 方式

CHANGE MASTER TO
  MASTER_AUTO_POSITION=1;
-- 自動找到正確的同步位置

啟用 GTID#

-- my.cnf
gtid_mode = ON
enforce_gtid_consistency = ON

GTID 的工作原理#

  1. 備庫把自己的 GTID 集合發給主庫
  2. 主庫計算差集,找出備庫缺少的事務
  3. 從第一個缺失事務開始發送 binlog
  4. 自動處理主備切換,無需手動找位點

一主多從架構#

架構圖#

flowchart TB
    M["主庫 A"]
    M -->|binlog| S1["備庫 A'"]
    M -->|binlog| S2["從庫 B"]
    M -->|binlog| S3["從庫 C"]

    style M fill:#fff3e0
    style S1 fill:#c8e6c9
    style S2 fill:#e3f2fd
    style S3 fill:#e3f2fd

主庫故障切換#

使用 GTID 模式時,從庫只需執行:

CHANGE MASTER TO
  MASTER_HOST='new_master_ip',
  MASTER_AUTO_POSITION=1;
START SLAVE;

系統自動計算同步位點,大大簡化了切換流程。

最佳實踐#

參數組態#

-- binlog 格式
binlog_format = ROW

-- 啟用 GTID
gtid_mode = ON
enforce_gtid_consistency = ON

-- 並行複製
slave_parallel_type = LOGICAL_CLOCK
slave_parallel_workers = 16

-- 備庫只讀
read_only = 1

監控指標#

指標說明告警閾值
seconds_behind_master主備延遲秒數> 30s
Slave_IO_RunningIO 線程狀態!= Yes
Slave_SQL_RunningSQL 線程狀態!= Yes

備庫設置只讀的原因#

  1. 防止誤操作
  2. 防止切換時雙寫
  3. 用於判斷節點角色

readonly 對 super 權限用戶無效,複製線程可以正常執行。

本章小結#

主題要點
複製原理io_thread 接收,sql_thread 執行
binlog 格式推薦 row,數據一致且可恢復
主備延遲大事務、單線程複製是主因
並行複製5.7.22 WRITESET 效果最好
切換策略可靠性優先,減少延遲
GTID自動位點,簡化切換

高可用的關鍵是減少主備延遲。延遲越小,切換越快,RPO 和 RTO 都更優。