Giao diện
9. Replication và CDC
Khi dữ liệu bắt đầu di chuyển: nhân bản database để scale đọc và chịu lỗi, và biến mọi thay đổi thành dòng sự kiện cho các hệ thống khác.
Replication để làm gì
- Tăng khả năng phục vụ truy vấn đọc (read replica).
- Tách tải phân tích, báo cáo khỏi database chính.
- Bản sao để backup mà không làm nặng source.
- Sẵn sàng cao: source hỏng thì đưa một replica lên thay (failover).
Cơ chế
- Binlog format: STATEMENT (ghi câu SQL, gọn nhưng có câu không tất định), ROW (ghi giá trị từng dòng thay đổi, mặc định từ 5.7.7, bắt buộc cho CDC), MIXED.
- GTID: mỗi transaction có định danh
server_uuid:số_thứ_tựduy nhất toàn cụm. Replica biết chính xác đã áp dụng tới đâu, nên failover và đổi source không cần dò vị trí file binlog.
Replication lag
Luôn có độ trễ giữa source và replica. Lag tăng khi replica không áp dụng kịp lượng thay đổi. Nguyên nhân hay gặp:
- Applier không đủ song song: source ghi bằng hàng trăm connection song song, replica phải áp dụng lại cùng lượng đó. Bật multi-threaded replica (
replica_parallel_workers, mặc định 4 từ 8.0.27). - Transaction lớn: DELETE hàng triệu dòng một lần chạy bao lâu trên source thì cũng chừng đó trên replica, và các transaction sau phải chờ. Chia batch.
- Bảng không có PRIMARY KEY với binlog ROW: mỗi UPDATE/DELETE trên replica có thể phải quét cả bảng để tìm dòng.
- DDL chạy lâu, replica bận phục vụ đọc nặng, I/O hoặc CPU của replica yếu hơn source, mạng chậm.
Thử: mô phỏng lag
Giả định để minh hoạ: mỗi applier thread áp dụng 400 tx/s, chạy song song đạt 75% hiệu suất. Transaction lớn chặn các transaction sau vì phải giữ thứ tự commit.
400tx/s replica áp dụng được
242 slag ở giây 120
Tụt lại
Source ghi 1.200 tx/s nhưng replica chỉ áp dụng được 400 tx/s. Backlog tăng đều, lag tăng mãi không tự hết. Tăng applier threads cho tới khi vượt tốc độ ghi.
Đọc lại thứ vừa ghi
User vừa sửa hồ sơ, tải lại trang đọc từ replica và thấy dữ liệu cũ. Cách xử lý:
- Đọc từ source trong vài giây sau khi user ghi.
- Lưu GTID sau khi ghi rồi đọc replica bằng
WAIT_FOR_EXECUTED_GTID_SET(gtid, timeout). - Sticky session cho các request ngay sau khi ghi.
Hệ thống lớn luôn giám sát lag và có chiến lược đọc phù hợp.
Async, semi-sync, group replication
Async (mặc định):
- Client gửi
COMMIT. - Source ghi binlog, commit InnoDB.
- Trả OK cho client.
- Source sập trước khi dump thread kịp gửi sự kiện.
- Failover lên replica: giao dịch client đã nhận OK không còn.
Semi-sync (AFTER_SYNC):
- Client gửi
COMMIT. - Source ghi và fsync binlog.
- Chờ ít nhất 1 replica ghi sự kiện vào relay log và gửi ACK.
- Commit InnoDB, trả OK cho client.
- Source sập: replica đã có sự kiện, failover không mất giao dịch.
Semi-sync thêm một vòng mạng vào mỗi commit. Quá rpl_semi_sync_source_timeout (mặc định 10 giây) không có ACK thì source tự hạ về async, nên cần giám sát.
Group Replication đi xa hơn: các node đồng thuận (Paxos) trước khi commit, tự bầu primary mới; là nền của InnoDB Cluster. Đổi lại latency commit cao hơn và cần mạng ổn định giữa các node.
Mô hình triển khai
- Một source, nhiều replica: phổ biến nhất.
- Replica phân tầng: replica có replica riêng, giảm tải gửi binlog cho source; lag cộng dồn qua các tầng.
- Hai source: thường chạy active–passive, chỉ một node nhận ghi, node kia bật
super_read_only. Ghi đồng thời cả hai rất dễ xung đột. - Nhiều source về một replica (multi-source): gom dữ liệu nhiều database để báo cáo.
- Replica trễ có chủ đích:
CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 3600giữ replica chậm 1 giờ, cứu được khi lỡ tay xoá dữ liệu.
Mô hình càng đơn giản càng dễ vận hành và an toàn.
CDC: vì sao theo dõi từng thay đổi
Khi hệ thống lớn dần, một thay đổi trong database kéo theo nhiều việc: đẩy bản ghi mới sang hệ thống phân tích, xoá cache để không trả dữ liệu cũ, publish sự kiện cho service khác, cấp dữ liệu cho pipeline realtime. Change Data Capture (CDC) ghi nhận từng thay đổi ngay khi nó xảy ra, như theo dõi một dòng log; khác với cron job hay ETL chạy theo lô mỗi giờ.
| Cách phát hiện thay đổi | Ưu điểm | Nhược điểm |
|---|---|---|
Cột audit (updated_at, version) + quét định kỳ | Dễ làm | Không thấy dòng bị xoá; phụ thuộc app cập nhật đúng; quét tốn tài nguyên; không realtime |
| So sánh snapshot | Chính xác | Phải lưu snapshot và quét toàn bảng mỗi lần; độ trễ lớn |
| Trigger ghi vào bảng thay đổi | Tự động, đủ INSERT/UPDATE/DELETE | Chạy đồng bộ trong transaction nên làm chậm mọi lệnh ghi; khó vận hành khi nhiều bảng |
| Đọc binlog (log-based) | Đủ mọi thay đổi kể cả DELETE, gần realtime, không sửa schema hay code app | Cần binlog ROW và binlog_row_image=FULL (binlog to hơn); thêm một dump thread; snapshot ban đầu quét toàn bảng; binlog phải giữ đủ lâu; cần hệ thống chuyên dụng để parse |
Log-based là chuẩn ở hệ thống lớn.
CDC hoạt động thế nào
- MySQL ghi mọi thay đổi vào binlog.
- Công cụ đọc binlog (Debezium) kết nối như một replica và nhận sự kiện liên tục.
- Mỗi thay đổi thành một bản ghi sự kiện có trạng thái trước, sau và metadata (vị trí binlog, GTID, thời gian).
- Sự kiện được publish vào message system, thường là Kafka.
- Các service downstream tiêu thụ theo nhu cầu.
json
{
"op": "u",
"before": { "id": 42, "status": 1 },
"after": { "id": 42, "status": 2 },
"source": { "table": "orders", "file": "binlog.000042", "pos": 1337,
"gtid": "3e11fa47-71ca-11e1-9e33-c80aa9429562:1234" },
"ts_ms": 1760342400000
}Ứng dụng của CDC
- Invalidate cache đúng lúc: mỗi thay đổi sinh sự kiện để xoá key tương ứng (Cache).
- Streaming sang kho phân tích (data warehouse, data lake) gần realtime, không cần job ETL nặng.
- Outbox pattern: giải bài toán “ghi DB rồi publish message”. Nếu làm hai bước riêng, publish thất bại sau khi ghi DB là mất sự kiện.
outbox trong cùng một transaction: cả hai cùng commit hoặc cùng không. CDC đọc bảng outbox và publish. Bảo đảm at-least-once: không mất sự kiện, nhưng có thể gửi trùng khi connector khởi động lại, nên consumer phải idempotent (bỏ qua event_id đã xử lý).Khi schema thay đổi
Thường tương thích:
- Thêm cột nullable hoặc có default.
- Mở rộng kiểu số (TINYINT → INT → BIGINT).
- Đổi qua lại CHAR và VARCHAR, tăng độ dài.
- Thêm giá trị ENUM vào cuối.
- Một số chuyển đổi kiểu thời gian.
Dễ làm gãy luồng CDC:
- Đổi cột nullable thành NOT NULL không có default.
- Đổi sang kiểu không chuyển đổi được.
- Đổi tên hoặc xoá cột mà consumer đang dùng.
- Công cụ online DDL (gh-ost, pt-osc) tạo bảng tạm: cần cấu hình connector bỏ qua.
Thay đổi gây gãy cần phối hợp giữa team sở hữu dữ liệu, team hạ tầng và các consumer; schema registry giúp kiểm tra tương thích trước khi triển khai.
CDC và replication khác nhau thế nào
| Replication | CDC | |
|---|---|---|
| Đầu vào | Binlog | Binlog |
| Đầu ra | Bản sao đầy đủ của database MySQL | Dòng sự kiện thay đổi cho hệ thống khác |
| Phục vụ | Scale đọc, tách tải phân tích, backup, failover | Đồng bộ cache, outbox, tích hợp microservice, streaming sang kho phân tích |
| Người dùng đầu ra | Chính MySQL (replica) | Kafka, search engine, warehouse, service khác |
Hệ thống lớn thường dùng cả hai: replication để phục vụ đọc, CDC để tạo dòng sự kiện realtime.
Ghi nhớ
- Replication mặc định async: failover có thể mất giao dịch; semi-sync khắc phục với giá một vòng mạng.
- Lag do applier, transaction lớn, bảng không PK; đọc-sau-ghi cần chiến lược riêng.
- CDC log-based là chuẩn; outbox = ghi cùng transaction + consumer idempotent.