GTID Replication 遷移步驟:MySQL 8.0 → 8.4
本地實測 MySQL 8.0 遷移至 8.4 步驟紀錄
[chiehting]
2056 Words CHANGE ME READ TIME9 Minutes, 20 Seconds
2026-09-20 00:00 +0800
目標要將 MySQL 8.0(下面統稱 M80),既有生產環境同步到 MySQL 8.4(下面統稱 M84),全新 instance,透過原生 GTID-based replication 完成資料搬遷與零停機切換。
準備 MySQL 服務
使用 podman compose up -d 命令運行 MySQL 服務。
services:
mysql84:
platform: linux/amd64
image: docker.io/mysql:8.4.9
container_name: mysql84
ports:
- "3306:3306"
volumes:
- './mysql/data84:/var/lib/mysql'
- './sql:/opt/sql'
environment:
MYSQL_ROOT_PASSWORD: password
mysql80:
platform: linux/amd64
image: docker.io/mysql:8.0.46
container_name: mysql80
ports:
- "3307:3306"
volumes:
- './mysql/data80:/var/lib/mysql'
- './sql:/opt/sql'
environment:
MYSQL_ROOT_PASSWORD: password
前置盤點
確認 M84 的字元集 / collation 跟 M80 一致
盤點 M80 的 schema、stored procedure/function/trigger,是否用到 8.4 版本已移除的語法(
CHANGE MASTER TO、SHOW SLAVE STATUS等 master/slave 系列指令)。utf8= utf8mb3 的 alias 棄用中,未來版本可能移除M84 建立時,參數群組 / my.cnf 直接帶入:
gtid_mode = ON enforce_gtid_consistency = ON select @@gtid_mode; select @@enforce_gtid_consistency;M84 是全新 instance,不需要走 Phase 2 的線上轉換流程。
Phase 1: 確認 M80 端 binlog 保留時間,足以蓋過整個遷移視窗
保留時間至少要涵蓋「全量匯出 + 傳輸 + 匯入 」的總時間,抓有緩衝的數字(目前一天全備份一次,所以保留時間設置為 24h)。依的部署方式選一種查/設方式:
M80 架設於 AWS RDS for MySQL
注意:
binlog_expire_logs_seconds在 RDS 上只是引擎層的上限,真正決定清除時機的是下面這個 RDS 平台層設定,兩者是分開的機制。
CALL mysql.rds_show_configuration; -- 查(預設 NULL = 0 小時)
CALL mysql.rds_set_configuration('binlog retention hours', 24); -- 設,上限 168 小時
M80 架設於 Huawei Cloud RDS for MySQL
注意:
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds'在 Huawei RDS 上查到的值官方文件明確說「不能拿來當參考」,一定要用主控台或 API 查真正的值。
GET /v3/{project_id}/instances/{instance_id}/binlog/clear-policy # 查(預設常見 0~3 小時)
PUT /v3/{project_id}/instances/{instance_id}/binlog/clear-policy
{ "binlog_retention_hours": 24 } # 設,範圍 0~168
或主控台:「資料備份 → Binlog 清理設定 → 本機保留時間」。
Phase 2: M80 端 GTID 模式轉換(若尚未是 ON 才需要走)
若 M80 本來就已經是 gtid_mode=ON / enforce_gtid_consistency=ON,這個 Phase 整段跳過。
若 M80 目前是 gtid_mode=OFF 或 OFF_PERMISSIVE、enforce_gtid_consistency=OFF,必須線上逐步轉換,不能直接跳到 ON:
- 全拓樸(M80 + 所有既有 replica)先設:
SET PERSIST enforce_gtid_consistency = WARN;
觀察 error log 是否出現 Statement violates GTID consistency 之類警告(常見來源:交易內 CREATE/DROP TEMPORARY TABLE、CREATE TABLE ... SELECT、混用 transactional/non-transactional 引擎),抓出來修正應用程式或批次腳本。
- 確認乾淨後,全拓樸改:
SET PERSIST enforce_gtid_consistency = ON;
- 全拓樸依序轉換
gtid_mode(一次只能走相鄰一步):
-- 全拓樸先切到 OFF_PERMISSIVE(若目前已是這個值可跳過)
SET PERSIST gtid_mode = OFF_PERMISSIVE;
-- 在「每一台」節點(M80 本身 + 所有既有 replica)都要跑,全部歸 0 才能往下走
SHOW STATUS LIKE 'ONGOING_ANONYMOUS_TRANSACTION_COUNT'; -- 要是 0
-- 在每一台都通過後,才切到 ON_PERMISSIVE
SET PERSIST gtid_mode = ON_PERMISSIVE;
-- 在 source(M80)上取得目前 binlog 座標:
SHOW BINARY LOG STATUS; -- 8.0 用 SHOW MASTER STATUS
-- 記下 File、Position,在「每一台」既有 replica 上執行
-- (逐層做:relay 拓樸要一層一層驗證,每台只對自己的直屬上游做)
SELECT SOURCE_POS_WAIT('記下的File', 記下的Position);
-- 在全部節點通過後,才轉最後一步
SET PERSIST gtid_mode = ON;
Phase 3: 建立複製帳號、確認網路連通
在 M80 上:
CREATE USER 'repl'@'%' IDENTIFIED WITH caching_sha2_password BY 'repl';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
確認 M80、M84 之間網路已打通(security group / VPC peering / 防火牆規則),M84 能連到 M80 的 3306。
Phase 4: 全量資料搬遷
資料量在數十 GB 內,mysqldump 可接受:
mysqldump -h M80_host -u root -p \
--single-transaction \
--set-gtid-purged=ON \
--triggers --routines --events \
--all-databases > full_backup.sql
mysql -h M84_host -u root -p < full_backup.sql
--single-transaction:InnoDB 一致性快照,避免長時間鎖表--set-gtid-purged=ON:把 dump 當下 M80 的gtid_executed寫入 dump 檔開頭,匯入 M84 後 M84 就知道「這些 GTID 我已經有了」,之後只會同步後續新增的部分--triggers --routines --events:加入 dump 條件,漏加會少物件
若資料量到百 GB 以上等級,改用支援平行處理的工具(Percona XtraBackup 或 mydumper/myloader),銜接 GTID 的邏輯相同,只是備份手法不同。
Phase 5: M84 端啟動複製
-- server_id 主從不能一樣,要檢查 server_id 參數
SHOW VARIABLES LIKE 'server_id';
-- 如果一樣要調整其中一台的 server_id,可以動態調整
-- 重啟後 server_id 會還原成設定檔的值。若正式環境要沿用這個 `server_id`,記得同步寫進參數群組
SET GLOBAL server_id = 2;
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='mysql80',
SOURCE_USER='repl',
SOURCE_PASSWORD='repl',
SOURCE_AUTO_POSITION=1;
START REPLICA;
Phase 6: 監控與資料一致性驗證
SHOW REPLICA STATUS\G
重點看:
Replica_IO_Running/Replica_SQL_Running都要YesLast_IO_Error/Last_SQL_Error是空的Seconds_Behind_Source追到 0 或穩定在很小的值
複製執行緒正常不代表資料真的一致,建議額外用 pt-table-checksum 或自寫 row count / checksum 腳本比對 M80、M84 兩端。
Phase 7:Cutover(零停機切換)
流量切換可以直接透過你們正在建的 ProxySQL-on-K8s 層(固定 endpoint、read/write split、自動 failover)來做,不需要另外設計新的代理機制——client 端本來就是接這個固定 endpoint,不需要改連線設定:
- 先觀察一段時間確認 M84 複製穩定、資料比對一致
- 透過 ProxySQL 先把唯讀流量導到 M84,觀察應用行為正常
- 確認無誤後,再把寫入流量切到 M84(此時 M80 停止接受寫入)
- 保留 M80 一段觀察期作為 fallback,不要切完立刻下線
- 切完後反向確認 M80 上沒有殘留的新寫入(避免有漏切的連線路徑繞過 ProxySQL 直連 M80)
Phase 8:收尾
- M84 穩定運行、觀察期結束後,拆除 M80 → M84 的複製關係
- 把 Phase 1 為了遷移臨時拉高的 binlog 保留時間調回正常值,避免長期占用多餘儲存空間
- 收回/刪除臨時建立的
repl複製帳號權限(或改成最小權限保留供未來使用) - 更新監控、備份策略、runbook 文件,指向新的 M84 作為正式環境