MySQL Semi-synchronous Replication: Cấu hình Zero Data Loss không cần Group Replication

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

Replication async mặc định của MySQL có một điểm yếu chết người: master commit xong là “xong chuyện”, không quan tâm slave đã nhận được data chưa. Nếu master sập đúng lúc đó, bạn mất dữ liệu.

Mình từng gặp đúng cái này lúc 3 giờ sáng — server crash, slave lag 15 giây, và 15 giây giao dịch đó bay sạch. Từ đó mình bắt đầu tìm cách khác, và mình từng gặp sự cố database corruption lúc 3 giờ sáng và phải restore từ backup — từ đó mình luôn kiểm tra backup hàng ngày và không bao giờ dùng async thuần cho database giao dịch nữa.

Semi-synchronous Replication sinh ra để giải quyết đúng vấn đề này — và nó đã có sẵn trong MySQL từ bản 5.5, không cần cài thêm bất kỳ phần mềm nào.

Ba cách tiếp cận Replication — Cái nào phù hợp?

Trước khi đi vào cấu hình, cần hiểu rõ đang chọn giữa 3 hướng nào và mỗi hướng đánh đổi gì:

1. Asynchronous Replication (mặc định)

Master commit → trả kết quả về client ngay. Slave nhận binlog theo kiểu “khi nào rảnh thì nhận”. Đây là cấu hình mặc định khi setup master-slave thông thường.

  • Tốc độ: Nhanh nhất, master không bị block bởi slave
  • Rủi ro: Mất dữ liệu nếu master crash trước khi slave kịp nhận binlog
  • Phù hợp: Read replica thuần túy, slave dùng cho reporting, ứng dụng chịu được mất vài giây data

2. Semi-synchronous Replication

Master commit → đợi ít nhất 1 slave xác nhận đã nhận binlog vào relay log → mới trả kết quả về client. Slave chưa cần apply xong query, chỉ cần ghi vào relay log là đủ.

  • Tốc độ: Chậm hơn async một chút — thêm 1 round-trip mạng, thường 1–5ms trên LAN
  • An toàn: Zero data loss — data đã có ở ít nhất 2 nơi trước khi commit thành công
  • Phù hợp: Production cần độ tin cậy cao, không muốn setup Group Replication phức tạp

3. Group Replication / InnoDB Cluster

Multi-master, tự động failover, dùng giao thức consensus Paxos. Giải pháp mạnh nhất nhưng cũng phức tạp nhất về vận hành.

  • Tốc độ: Phụ thuộc số node và network latency, thường chậm hơn semi-sync
  • An toàn: Cao nhất, tự động xử lý split-brain và failover
  • Phù hợp: Cần HA hoàn toàn tự động, team có kinh nghiệm vận hành distributed system

Phân tích ưu và nhược của Semi-sync

Sau khi thử cả 3 trên môi trường production thực tế, đây là điểm cân bằng thực sự của từng giải pháp:

  • Data loss risk: Async = có | Semi-sync = gần như không | Group Replication = không
  • Write latency tăng thêm: Async = 0ms | Semi-sync = +1–5ms (LAN) | Group Replication = +5–20ms
  • Độ phức tạp setup: Async = thấp | Semi-sync = thấp | Group Replication = cao
  • Tự động failover: Async = không | Semi-sync = không | Group Replication = có
  • Số node tối thiểu: Async = 2 | Semi-sync = 2 | Group Replication = 3 (số lẻ)

Kết luận thực tế: Semi-sync là điểm cân bằng tốt nhất cho đa số trường hợp production — có zero data loss với effort setup tối thiểu. Nếu team chưa sẵn sàng vận hành Group Replication, Semi-sync là lựa chọn đúng đắn.

Khi nào nên (và không nên) dùng Semi-sync

Semi-sync phù hợp khi có ít nhất một trong các điều kiện sau:

  • Database lưu giao dịch tài chính, đơn hàng — tuyệt đối không được mất data
  • Đã có Master-Slave async sẵn, muốn nâng cấp mà không cần rebuild từ đầu
  • Network latency giữa master và slave dưới 10ms (cùng datacenter hoặc cùng AZ)
  • Team chưa có kinh nghiệm vận hành Group Replication

Không nên dùng khi: Master và slave ở 2 datacenter khác nhau với latency cao (mỗi write sẽ chậm lại tương ứng), hoặc ứng dụng có write throughput cực cao (hàng nghìn TPS) không chịu được thêm latency dù chỉ vài ms.

Hướng dẫn cấu hình MySQL Semi-synchronous Replication

Yêu cầu trước khi bắt đầu

  • MySQL 5.7+ (khuyến nghị MySQL 8.0)
  • Đã có Master-Slave replication async đang chạy ổn định
  • InnoDB storage engine (bắt buộc với semi-sync)
  • Kết nối mạng ổn định giữa master và slave, latency thấp

Nếu chưa có replication cơ bản, đọc bài “Cấu hình MySQL replication Master-Slave” trước rồi quay lại đây. Semi-sync chỉ là một lớp bổ sung lên trên cấu hình có sẵn.

Bước 1: Enable Semi-sync plugin trên Master

Semi-sync dùng plugin built-in của MySQL, không cần cài thêm package nào từ bên ngoài.

-- Kết nối vào MySQL master
-- Cài plugin semi-sync cho master
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';

-- Enable semi-sync
SET GLOBAL rpl_semi_sync_master_enabled = 1;

-- Timeout (ms) trước khi fallback về async nếu slave không phản hồi
-- 10000ms = 10 giây, điều chỉnh tùy yêu cầu của bạn
SET GLOBAL rpl_semi_sync_master_timeout = 10000;

-- Số slave tối thiểu phải ACK trước khi master commit (mặc định là 1)
SET GLOBAL rpl_semi_sync_master_wait_for_slave_count = 1;

-- Kiểm tra cấu hình vừa set
SHOW VARIABLES LIKE 'rpl_semi_sync%';

Để cấu hình persist qua lần restart, thêm vào /etc/mysql/mysql.conf.d/mysqld.cnf trên master:

[mysqld]
plugin-load-add = semisync_master.so
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_timeout = 10000
rpl_semi_sync_master_wait_for_slave_count = 1

Bước 2: Enable Semi-sync plugin trên Slave

Làm tương tự trên từng slave node:

-- Kết nối vào MySQL slave
-- Cài plugin semi-sync cho slave
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

-- Enable semi-sync slave
SET GLOBAL rpl_semi_sync_slave_enabled = 1;

-- Restart IO thread để kết nối lại với master theo mode semi-sync
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;

Thêm vào mysqld.cnf trên slave để persist:

[mysqld]
plugin-load-add = semisync_slave.so
rpl_semi_sync_slave_enabled = 1

Lưu ý với MySQL 8.0.26+: Tên plugin thay đổi từ master/slave sang source/replica:

-- Trên MySQL 8.0.26 trở lên
-- Master node
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
SET GLOBAL rpl_semi_sync_source_enabled = 1;

-- Slave node
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';
SET GLOBAL rpl_semi_sync_replica_enabled = 1;

Bước 3: Kiểm tra Semi-sync có hoạt động không

-- Chạy trên MASTER
SHOW STATUS LIKE 'Rpl_semi_sync%';

-- Các giá trị quan trọng cần chú ý:
-- Rpl_semi_sync_master_status  = ON   -- Semi-sync đang active
-- Rpl_semi_sync_master_clients = 1    -- Có 1 slave đang kết nối semi-sync
-- Rpl_semi_sync_master_yes_tx  = 100  -- Số transaction đã commit kèm ACK từ slave
-- Rpl_semi_sync_master_no_tx   = 0    -- Số transaction phải fallback về async (mong muốn = 0)
-- Chạy trên SLAVE
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';

-- Output mong muốn:
-- Rpl_semi_sync_slave_status = ON

Nếu Rpl_semi_sync_master_clients = 0, nghĩa là slave chưa kết nối lại sau khi enable semi-sync. Chạy lại STOP SLAVE IO_THREAD; START SLAVE IO_THREAD; trên slave.

Bước 4: Test thực tế — Giả lập slave offline

Test để xác nhận semi-sync đang hoạt động đúng như kỳ vọng:

# Terminal 1: Trên slave — stop IO thread để giả lập slave bị ngắt kết nối
mysql -u root -p -e "STOP SLAVE IO_THREAD;"

# Terminal 2: Trên master — thử insert và đo thời gian phản hồi
time mysql -u root -p -e "
  INSERT INTO testdb.orders (user_id, amount, created_at)
  VALUES (1, 99.99, NOW());
"
# Lệnh này block khoảng 10 giây (bằng timeout đã đặt) rồi mới thành công
# Master tự động fallback về async sau khi hết timeout

# Terminal 1: Bật lại slave IO thread
mysql -u root -p -e "START SLAVE IO_THREAD;"

# Kiểm tra số transaction đã fallback về async trên master
mysql -u root -p -e "SHOW STATUS LIKE 'Rpl_semi_sync_master_no_tx';"
# Giá trị sẽ tăng lên 1 — đây là transaction vừa chạy ở chế độ async

Hiểu về Semi-sync Fallback và cách monitor

Khi slave không phản hồi trong thời gian timeout (rpl_semi_sync_master_timeout), master tự động fallback về async để tránh block ứng dụng. Đây là behavior có chủ đích — tránh production đứng cứng vì slave có vấn đề.

Tuy nhiên trong khoảng thời gian fallback đó, hệ thống đang chạy ở chế độ async và nguy cơ mất data quay trở lại. Cần monitor chỉ số này liên tục:

#!/bin/bash
# Script monitor semi-sync status — chạy qua cron mỗi phút
MYSQL_CMD="mysql -u monitor_user -pYOUR_PASSWORD -N -e"

STATUS=$($MYSQL_CMD "SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Rpl_semi_sync_master_status';")
NO_TX=$($MYSQL_CMD "SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Rpl_semi_sync_master_no_tx';")

if [ "$STATUS" != "ON" ]; then
  echo "ALERT: Semi-sync OFF — dang chay async mode!"
  # Thêm lệnh gửi alert Slack/Telegram/PagerDuty tại đây
fi

if [ "$NO_TX" -gt "0" ]; then
  echo "WARNING: Co $NO_TX transaction da fallback ve async"
fi

Thêm vào crontab:

* * * * * /opt/scripts/check_semisync.sh >> /var/log/semisync_monitor.log 2>&1

Tổng kết

Semi-synchronous Replication là nâng cấp đơn giản từ async replication mặc định, mang lại zero data loss mà không cần rebuild hạ tầng hay học Group Replication. Với hệ thống đã có Master-Slave, bạn enable được trong vòng 10 phút.

Hai chỉ số cần nhớ theo dõi hàng ngày: Rpl_semi_sync_master_status phải là ON, và Rpl_semi_sync_master_no_tx phải luôn ở mức 0. Khi hai giá trị này bất thường, hệ thống đang chạy ở chế độ kém an toàn hơn và cần điều tra ngay — đừng để đến 3 giờ sáng mới phát hiện.

Share: