Cấu hình MySQL Replication Master-Slave: Từ Sự Cố Thực Tế Đến Giải Pháp

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

Chuyện xảy ra lúc 3 giờ sáng

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. Nhưng backup tốt đến đâu cũng có một lỗ hổng chết người: trong lúc restore, website chết hoàn toàn. Khách hàng không mua được hàng, SEO bị ảnh hưởng, và bạn ngồi đó nhìn màn hình đen, uống cà phê nguội.

Sau sự cố đó, mình bắt đầu tìm hiểu nghiêm túc về MySQL Replication — và đây là thứ mình ước mình biết sớm hơn vài năm.

Tại sao database lại là điểm yếu nhất?

Nhìn vào 9/10 web app nhỏ và vừa, bạn sẽ thấy cùng một sơ đồ: 1 web server + 1 database server. Setup này chạy ổn khi traffic còn nhỏ. Nhưng khi người dùng tăng, database luôn là thứ đầu tiên chịu trận.

Vấn đề cụ thể:

  • Read-heavy workload: 80-90% query trên web thông thường là SELECT (đọc dữ liệu). Tất cả đều đổ vào 1 server duy nhất.
  • Single point of failure: Database chết = toàn bộ ứng dụng chết. Không có plan B.
  • Backup không bảo vệ được downtime: Restore từ backup mất 30 phút đến vài giờ tùy kích thước data.
  • Maintenance gây downtime: Cần upgrade MySQL version? Cần tối ưu bảng lớn? Toàn bộ phải ngừng hoạt động.

Các cách giải quyết — và vấn đề của từng cách

Scale up: Nâng cấp phần cứng

Cách đơn giản nhất: mua server xịn hơn. Nhiều RAM hơn, SSD nhanh hơn, nhiều core hơn. Nhưng scale up chỉ kéo dài thêm thời gian — không thể nâng cấp mãi mãi. Một instance r6g.8xlarge trên AWS (64GB RAM, 32 vCPU) tốn khoảng $1,000/tháng, và vẫn là single point of failure.

Thêm caching layer (Redis/Memcached)

Redis hay Memcached có thể giảm 60-70% số query đổ xuống database — đặc biệt hiệu quả cho trang listing, search result, hay static content. Nhưng không phải thứ gì cũng cache được. Giỏ hàng đang cập nhật, số dư tài khoản, hay thông báo real-time — những thứ đó cần đọc thẳng từ database.

MySQL Replication

Database tự động nhân bản sang server khác, liên tục, gần như real-time. Vừa chia tải đọc/ghi, vừa có sẵn bản dự phòng — đây mới là xử lý tận gốc.

MySQL Replication hoạt động như thế nào?

Ý tưởng đơn giản: có 1 server Master nhận tất cả các thao tác ghi (INSERT, UPDATE, DELETE). Mọi thay đổi đều được ghi vào Binary Log. Các server Slave kết nối vào Master, đọc Binary Log và thực thi lại các câu lệnh đó trên chính mình.

Kết quả: Slave luôn có bản copy gần giống hệt Master, chỉ trễ vài millisecond đến vài giây tùy tải.

Với setup này, ứng dụng của bạn có thể:

  • Gửi tất cả query ghi → Master
  • Gửi tất cả query đọc → Slave(s)

Ngay lập tức giảm 80% tải trên Master, và có sẵn 1 bản backup “nóng” luôn cập nhật.

Cấu hình MySQL Replication Master-Slave từng bước

Môi trường demo: Ubuntu 22.04, MySQL 8.0, 2 server.

  • Master: IP 192.168.1.10
  • Slave: IP 192.168.1.20

Bước 1: Cấu hình MySQL trên Master

Mở file cấu hình MySQL:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Thêm hoặc chỉnh sửa các dòng sau:

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

Khởi động lại MySQL:

sudo systemctl restart mysql

Bước 2: Tạo user replication trên Master

Đăng nhập vào MySQL:

mysql -u root -p

Tạo user và cấp quyền:

CREATE USER 'replica_user'@'192.168.1.20' IDENTIFIED WITH mysql_native_password BY 'MatKhauManh123!';
GRANT REPLICATION SLAVE ON *.* TO 'replica_user'@'192.168.1.20';
FLUSH PRIVILEGES;

Lấy thông tin Binary Log hiện tại — giữ lại con số này, cần dùng ở bước sau:

SHOW MASTER STATUS;

Output trông như thế này:

+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 |      154  | mydb         |                  |
+------------------+----------+--------------+------------------+

Ghi lại FilePosition.

Bước 3: Export dữ liệu từ Master

Lock bảng, dump data, rồi unlock:

# Trong session MySQL, lock bảng
FLUSH TABLES WITH READ LOCK;

# Mở terminal khác, dump database
mysqldump -u root -p --databases ten_database_cua_ban > /tmp/db_dump.sql

# Quay lại session đầu, unlock
UNLOCK TABLES;

Copy file dump sang Slave:

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

Bước 4: Cấu hình MySQL trên Slave

Mở file cấu hình:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Thêm:

[mysqld]
server-id = 2
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = ten_database_cua_ban
relay-log = /var/log/mysql/mysql-relay-bin.log

Khởi động lại MySQL và import dữ liệu:

sudo systemctl restart mysql
mysql -u root -p < /tmp/db_dump.sql

Bước 5: Kết nối Slave vào Master

Đăng nhập MySQL trên Slave:

STOP SLAVE;

CHANGE MASTER TO
  MASTER_HOST='192.168.1.10',
  MASTER_USER='replica_user',
  MASTER_PASSWORD='MatKhauManh123!',
  MASTER_LOG_FILE='mysql-bin.000003',
  MASTER_LOG_POS=154;

START SLAVE;

Thay mysql-bin.000003154 bằng giá trị bạn ghi lại ở Bước 2.

Bước 6: Kiểm tra kết quả

Chạy lệnh này trên Slave:

SHOW SLAVE STATUS\G

Hai dòng quan trọng cần kiểm tra:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

Nếu cả hai đều là Yes — replication đang chạy. Thử tạo bảng mới hoặc insert dữ liệu trên Master, rồi kiểm tra trên Slave xem có xuất hiện không.

Những điều cần lưu ý khi vận hành

Replication lag — Slave không phải lúc nào cũng có dữ liệu 100% giống Master. Nếu ứng dụng cần đọc dữ liệu ngay sau khi ghi (ví dụ: user đăng ký xong → lập tức xem profile), hãy route request đó sang Master để đọc.

Slave là read-only — Đừng để ứng dụng vô tình ghi dữ liệu vào Slave. Thêm dòng này vào config Slave để an toàn:

read_only = ON

Giám sát replication — Thỉnh thoảng replication bị dừng vì lỗi (thường là data conflict). Nên có script check định kỳ:

mysql -u root -p -e "SHOW SLAVE STATUS\G" | grep -E "Running|Error|Seconds_Behind"

Firewall — Đảm bảo port 3306 trên Master cho phép kết nối từ IP của Slave. Đừng mở cho toàn bộ internet.

sudo ufw allow from 192.168.1.20 to any port 3306

Kết quả thực tế sau khi triển khai

Sau khi setup replication cho một dự án e-commerce có ~50k session/ngày, tải trên Master giảm từ 85% CPU xuống còn 30%. Query time trung bình cho các trang listing sản phẩm giảm từ 800ms xuống 200ms — chỉ nhờ route read query sang Slave.

Và quan trọng hơn: lần tiếp theo cần maintenance database, mình chỉ cần promote Slave lên làm Master tạm thời, không mất một giây downtime nào.

Không phức tạp, không tốn kém. Chỉ cần một VPS $5-10/tháng và 30 phút cấu hình. Đổi lại: database nhẹ hơn hẳn, có sẵn bản dự phòng luôn cập nhật, và lần sau maintenance không còn phải lo downtime. Đáng để làm ngay hôm nay.

Share: