Hướng dẫn cấu hình MySQL Replication dựa trên GTID: Đồng bộ dữ liệu nâng cao và Failover tự động

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

Replication truyền thống gây đau đầu — GTID giải quyết được gì?

Nếu bạn đã từng cấu hình MySQL Master-Slave theo kiểu cũ — dựa vào binlog_file + binlog_pos — thì hẳn biết cảm giác hồi hộp khi cần promote Slave lên làm Master mới. Phải tìm đúng vị trí binlog. Nhỡ sai một chút là replication bị lệch, dữ liệu mất đồng bộ mà không hay.

Mình từng gặp database corruption lúc 3 giờ sáng, phải restore từ backup mất gần 2 tiếng. Sau lần đó mình chuyển toàn bộ hệ thống sang GTID — vì với GTID, mỗi transaction có một định danh toàn cục duy nhất. MySQL tự biết cái nào đã chạy, cái nào chưa. Không cần bạn nhớ số file và offset.

GTID (Global Transaction Identifier) có format: source_id:transaction_id, ví dụ 3E11FA47-71CA-11E1-9E33-C80AA9429562:23. Khi failover, Slave mới chỉ cần kết nối vào Master mới — MySQL tự tính phần còn thiếu và đồng bộ tiếp, không cần can thiệp thủ công.

Chuẩn bị môi trường

Bài này dùng 2 server MySQL 8.0 chạy Ubuntu 22.04:

  • Master: 192.168.1.10
  • Slave: 192.168.1.11

Cài MySQL 8.0 trên cả hai server:

sudo apt update
sudo apt install -y mysql-server-8.0
sudo systemctl enable --now mysql

Kiểm tra version:

mysql --version
# mysql  Ver 8.0.36-0ubuntu0.22.04.1 for Linux on x86_64 ((Ubuntu))

Cấu hình GTID Replication chi tiết

Bước 1: Cấu hình Master (192.168.1.10)

Chỉnh file /etc/mysql/mysql.conf.d/mysqld.cnf:

[mysqld]
server-id                = 1
bind-address             = 0.0.0.0
log_bin                  = /var/log/mysql/mysql-bin.log
binlog_format            = ROW

# GTID settings
gtid_mode                = ON
enforce_gtid_consistency = ON

# Recommended for GTID
log_slave_updates        = ON
binlog_expire_logs_seconds = 604800   # giữ binlog 7 ngày

Khởi động lại MySQL:

sudo systemctl restart mysql

Tạo user riêng cho replication — đừng dùng root, hạn chế quyền cho đúng IP Slave:

CREATE USER 'replicator'@'192.168.1.11' IDENTIFIED WITH mysql_native_password BY 'StrongPass!2024';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'192.168.1.11';
FLUSH PRIVILEGES;

Dump snapshot từ Master. Dùng --single-transaction để không lock table — quan trọng trên production vì lock toàn bộ DB có thể làm ứng dụng treo:

mysqldump \
  --single-transaction \
  --master-data=2 \
  --set-gtid-purged=ON \
  --all-databases \
  -u root -p \
  > /tmp/master_dump.sql

Copy dump sang Slave:

scp /tmp/master_dump.sql [email protected]:/tmp/

Bước 2: Cấu hình Slave (192.168.1.11)

Tương tự Master, nhưng thêm read_only để tránh ghi nhầm lên Slave — lỗi này khá phổ biến khi developer kết nối nhầm server:

[mysqld]
server-id                = 2
bind-address             = 0.0.0.0
log_bin                  = /var/log/mysql/mysql-bin.log
binlog_format            = ROW

# GTID settings
gtid_mode                = ON
enforce_gtid_consistency = ON
log_slave_updates        = ON

# Slave-specific: chặn ghi để tránh ghi nhầm
read_only                = ON
super_read_only          = ON
sudo systemctl restart mysql

Import dump từ Master:

mysql -u root -p < /tmp/master_dump.sql

Kết nối Slave vào Master. Chú ý SOURCE_AUTO_POSITION = 1 — đây chính là điểm khác biệt so với replication cũ, không cần chỉ định file hay offset:

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST      = '192.168.1.10',
  SOURCE_PORT      = 3306,
  SOURCE_USER      = 'replicator',
  SOURCE_PASSWORD  = 'StrongPass!2024',
  SOURCE_AUTO_POSITION = 1;

START REPLICA;

Kiểm tra đồng bộ và Monitoring

Kiểm tra trạng thái Slave

SHOW REPLICA STATUS\G

Ba dòng cần để ý:

Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0

Nếu Seconds_Behind_Source tăng dần thay vì về 0 — Slave đang bị lag, cần kiểm tra tải CPU/disk trên cả hai server.

So sánh GTID giữa Master và Slave để xác nhận đồng bộ hoàn toàn:

-- Trên Master
SHOW MASTER STATUS\G
-- Executed_Gtid_Set: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-150

-- Trên Slave
SELECT @@GLOBAL.gtid_executed;
-- Kết quả phải khớp với Master

Test nhanh: ghi dữ liệu trên Master, đọc trên Slave

-- Trên Master
CREATE DATABASE gtid_test;
USE gtid_test;
CREATE TABLE ping (id INT AUTO_INCREMENT PRIMARY KEY, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
INSERT INTO ping VALUES ();

-- Trên Slave (sau vài giây)
USE gtid_test;
SELECT * FROM ping;
-- Phải thấy row vừa insert

Script monitoring replication lag

Bỏ script này vào crontab, chạy mỗi 5 phút. Ngưỡng 30 giây phù hợp cho hầu hết hệ thống — nếu SLA của bạn chặt hơn, hạ xuống 10 giây:

#!/bin/bash
# /usr/local/bin/check_replica_lag.sh

LAG=$(mysql -u root -p'password' -e "SHOW REPLICA STATUS\G" 2>/dev/null \
      | grep Seconds_Behind_Source \
      | awk '{print $2}')

if [ "$LAG" = "NULL" ]; then
  echo "CRITICAL: Replication stopped!"
  # Gửi alert (email, Telegram, PagerDuty...)
elif [ "$LAG" -gt 30 ]; then
  echo "WARNING: Replication lag = ${LAG}s"
fi

Xử lý Failover với GTID

Kịch bản: Master chết đột ngột, cần promote Slave thành Master mới trong vài phút thay vì vài chục phút như replication cũ.

-- Trên Slave mới promote
STOP REPLICA;
RESET REPLICA ALL;

-- Tắt chế độ read-only
SET GLOBAL read_only = OFF;
SET GLOBAL super_read_only = OFF;

Cập nhật connection string của ứng dụng trỏ sang 192.168.1.11. Xong — thường mất dưới 5 phút nếu bạn đã chuẩn bị runbook trước.

Muốn thêm Slave mới (192.168.1.12) vào Master vừa promote? Cú pháp y hệt, GTID lo phần còn lại:

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST          = '192.168.1.11',
  SOURCE_USER          = 'replicator',
  SOURCE_PASSWORD      = 'StrongPass!2024',
  SOURCE_AUTO_POSITION = 1;
START REPLICA;

Không cần biết binlog file tên gì, offset ở đâu. MySQL tự tìm transaction còn thiếu dựa vào GTID set.

Một vài lỗi thường gặp khi dùng GTID

  • Error 1236 — Got fatal error from master: Slave yêu cầu binlog mà Master đã purge mất. Fix: dump lại từ Master, chạy RESET MASTER trên Slave trước khi import, rồi re-sync.
  • Error 1418 — This function has none of DETERMINISTIC: Thiếu khai báo DETERMINISTIC khi tạo stored function. Thêm keyword đó vào function, hoặc tạm thời set log_bin_trust_function_creators = ON nếu đang gấp.
  • Slave dừng do duplicate GTID: Hay xảy ra khi restore dump không đúng thứ tự. Chạy RESET MASTER trên Slave trước khi import dump là tránh được.

Setup GTID mất thêm 15-20 phút so với replication cũ. Đổi lại, failover từ hàng chục phút còn vài phút, và bạn sẽ không phải ngồi tra binlog position lúc 3 giờ sáng nữa.

Share: