PostgreSQL Bài 34: Streaming Replication và Kiến trúc High Availability (HA) với Master-Slave
Ở Bài 33, chúng ta đã biết WAL (Write-Ahead Log) là "chiếc hộp đen" lưu trữ mọi thay đổi vật lý của cơ sở dữ liệu để cứu hộ khi máy chủ sập nguồn. Nếu chúng ta có thể gửi liên tục các bản ghi WAL này qua mạng sang một máy chủ khác, và yêu cầu máy chủ đó "phát lại" (Replay) y hệt những gì máy chủ chính vừa làm, chúng ta sẽ có một bản sao hoàn hảo theo thời gian thực.
Đó chính là nguyên lý cốt lõi của Streaming Replication (Sao chép luồng) — nền tảng để xây dựng kiến trúc High Availability (HA - Độ sẵn sàng cao) và cân bằng tải truy vấn đọc (Read-Scale) trong PostgreSQL.
1. Kiến trúc Physical Replication: walsender và walreceiver
Streaming Replication trong PostgreSQL là cơ chế sao chép vật lý (Physical Replication). Nó copy chính xác từng byte dữ liệu (Block 8KB) từ máy chủ chính (Primary/Master) sang máy chủ phụ (Standby/Slave).
[ PRIMARY SERVER (Master) ] [ STANDBY SERVER (Slave) ]
(Chấp nhận Đọc & Ghi) (Chỉ chấp nhận Đọc - Hot Standby)
1. Client UPDATE data
│
2. Ghi WAL vào đĩa
│
▼
3. Tiến trình `walsender` ────────(Gửi WAL qua TCP)───────► 1. Tiến trình `walreceiver` nhận WAL
│
▼
2. Ghi WAL vào đĩa cục bộ của Slave
│
▼
3. Tiến trình `startup` phát lại (Redo)
áp dụng thay đổi lên Data Pages RAM
Các đặc điểm sống còn của Physical Replication:
-
Giống nhau 100% về vật lý: Máy Slave là một bản sao bit-by-bit của máy Master. Bạn không thể chỉ sao chép một bảng hay một database; bạn bắt buộc phải sao chép toàn bộ Cluster.
-
Hot Standby: Kể từ PostgreSQL 9.0, máy Slave không chỉ "câm lặng" nhận dữ liệu mà có thể mở kết nối cho Client thực hiện các câu lệnh
SELECT(gọi là chế độ Hot Standby), giúp gánh tải đọc cho Master. Slave tuyệt đối từ chối mọi lệnh ghi (INSERT/UPDATE/DELETE/DDL).
2. Asynchronous vs Synchronous Replication (Bất đồng bộ vs Đồng bộ)
Quyết định lớn nhất khi thiết lập Replication là chọn cách Master phản hồi cho Client khi có lệnh COMMIT.
2.1. Sao chép Bất đồng bộ (Asynchronous) - Mặc định
-
Quy trình: Khi Client gửi lệnh
COMMIT, Master ghi WAL xuống đĩa cục bộ của nó, sau đó trả về SUCCESS ngay lập tức cho Client. Cùng lúc đó,walsenderâm thầm gửi WAL sang Slave ở background. -
Ưu điểm: Hiệu năng cực cao, tốc độ ghi của Master không bị ảnh hưởng bởi độ trễ mạng (Network Latency) hay tốc độ ổ cứng của Slave.
-
Rủi ro (Data Loss): Nếu Master cúp điện hoặc cháy ổ cứng ngay sau khi báo SUCCESS cho Client, nhưng phần WAL đó chưa kịp truyền qua mạng sang Slave, dữ liệu đó sẽ bị mất vĩnh viễn khi bạn thăng cấp Slave lên làm Master mới.
2.2. Sao chép Đồng bộ (Synchronous)
Kích hoạt bằng tham số synchronous_commit = on và synchronous_standby_names = '...'.
-
Quy trình: Khi Client
COMMIT, Master ghi WAL cục bộ, gửi qua mạng, và phải CHỜ Slave phản hồi (ACK) rằng nó đã ghi WAL xuống đĩa thành công, rồi Master mới trả về SUCCESS cho Client. -
Ưu điểm: Đảm bảo Zero Data Loss (RPO = 0). Dữ liệu đã commit chắc chắn tồn tại ở ít nhất 2 máy chủ vật lý khác nhau.
-
Rủi ro (Performance Hit): Lệnh
COMMITsẽ chậm đi đáng kể bằng đúng độ trễ mạng vòng (Round-trip time) cộng với thời gian ghi đĩa của Slave. Nếu mạng bị đứt hoặc máy Slave chết, máy Master sẽ bị treo hoàn toàn mọi lệnh ghi (chờ vô tận) cho đến khi Slave kết nối lại.
| Tiêu chí | Asynchronous (Bất đồng bộ) | Synchronous ( Đồng bộ) |
|---|---|---|
| Bảo toàn dữ liệu khi Master chết | Có thể mất một lượng nhỏ (vài ms đến vài giây) | Bảo toàn 100% (Không mất dòng nào) |
| Tốc độ lệnh Ghi (COMMIT) | Nhanh tối đa | Phụ thuộc vào mạng và ổ cứng Slave |
| Khi Slave sập | Master vẫn hoạt động bình thường | Master bị treo (Block) mọi lệnh ghi |
| Ứng dụng | Log, Analytics, Mạng xã hội, E-commerce | Ngân hàng, Giao dịch ví điện tử, Rút tiền |
3. Độ trễ sao chép (Replication Lag) và Công cụ giám sát
Dù nhanh đến mấy, quá trình gửi WAL qua mạng và phát lại trên đĩa luôn tốn thời gian. Khoảng chênh lệch giữa mốc dữ liệu Master đã ghi và mốc dữ liệu Slave đã phát lại thành công gọi là Replication Lag.
- Nếu Client vừa đăng ký tài khoản trên Master (vào DB), sau đó 50ms hệ thống tự động redirect sang trang Profile nhưng query được điều hướng đọc từ Slave. Nếu độ trễ lúc đó là 100ms, query sẽ không tìm thấy User (lỗi
404 Not Found). Đây là bẫy kinh điển trong kiến trúc Read/Write Split.
Truy vấn giám sát trên Master:
Chạy lệnh này trên Master để xem danh sách các máy Slave đang kết nối và trạng thái lag:
SQL
SELECT
application_name AS standby_name,
state,
sync_state,
pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn) AS lag_bytes_sent,
pg_wal_lsn_diff(pg_current_wal_lsn(), write_lsn) AS lag_bytes_written,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes_replayed
FROM pg_stat_replication;
Chỉ số lag_bytes_replayed càng lớn, Slave càng bị tụt hậu xa so với Master.
4. Cơn đau đầu: Xung đột truy vấn (Query Conflicts) trên Slave
Bản chất của Streaming Replication là copy "mù" ở tầng khối lưu trữ (Block level). Điều này sinh ra một mâu thuẫn khổng lồ giữa Master và Slave.
Kịch bản xung đột:
-
Trên máy Slave, một nhà phân tích dữ liệu (Data Analyst) chạy một câu
SELECTbáo cáo phức tạp tốn 10 phút. Câu query này đang đọc các dòng dữ liệu của bảngorders. -
Đồng thời trên máy Master, một lệnh
DELETExóa các đơn hàng cũ, và tiến trìnhVACUUMchạy qua dọn dẹp (ghi đè bằng khoảng trống) để thu hồi dung lượng. Thay đổi này lập tức được ghi vào WAL và truyền sang Slave. -
Máy Slave nhận được chỉ thị từ WAL: "Hãy xóa bỏ dữ liệu ở Block số 100 ngay lập tức".
-
Mâu thuẫn: Nhưng câu lệnh
SELECT10 phút của nhà phân tích vẫn đang cần đọc Block 100 đó!
Cách PostgreSQL xử lý xung đột: Tham số max_standby_streaming_delay
PostgreSQL không cho phép Slave phân mảnh cấu trúc vật lý khác với Master. Do đó:
-
Nó sẽ cho phép câu lệnh
SELECTcủa nhà phân tích tiếp tục chạy, và tạm dừng việc phát lại WAL (kéo theo Replication Lag tăng mạnh). -
Nó sẽ chờ một khoảng thời gian tối đa là
max_standby_streaming_delay(mặc định là 30 giây). -
Nếu quá 30 giây mà câu lệnh
SELECTvẫn chưa xong, PostgreSQL sẽ "bóp cò" hủy ngang (Cancel) câu lệnhSELECTđó để nhường đường cho WAL phát lại, bảo vệ cấu trúc đồng bộ.
Người dùng sẽ nhận được mã lỗi vô cùng "ức chế":
Plaintext
FATAL: terminating connection due to conflict with recovery
DETAIL: User query might have needed to see row versions that must be removed.
(Kỹ thuật giảm thiểu: Bật tham số hot_standby_feedback = on để Slave báo cáo lại các ID transaction nó đang đọc về cho Master, yêu cầu Master hoãn việc VACUUM các dòng đó lại. Tuy nhiên, nó có thể làm bảng trên Master bị phình to (Table Bloat) nếu query trên Slave treo quá lâu).
5. Kiến trúc High Availability (HA) và Hiểm họa Split-Brain
Replication chỉ là nền tảng. Khi máy Master bị sập nguồn do sự cố phần cứng, PostgreSQL KHÔNG tự động biến máy Slave thành Master mới. Quá trình biến một Slave thành Master gọi là Failover.
Nếu bạn tự viết script Bash để làm việc này, bạn rất dễ rơi vào thảm họa Split-Brain (Tâm thần phân liệt hệ thống).
Hiểm họa Split-Brain:
-
Đứt cáp mạng giữa Master và Slave. Master vẫn sống, Slave vẫn sống, nhưng chúng không nhìn thấy nhau.
-
Script theo dõi của bạn nằm bên phía Slave thấy Master không phản hồi, liền tự động thăng cấp Slave lên làm Master mới.
-
Lúc này, bạn có hai máy Master trong cùng một hệ thống! Router ứng dụng vẫn trỏ một nửa request về Master cũ, một nửa về Master mới. Dữ liệu phân tách thành 2 nhánh hoàn toàn khác biệt. Khi cáp mạng thông lại, bạn vĩnh viễn không thể merge (gộp) lại dữ liệu của hai máy này!
Hệ sinh thái công cụ chuẩn HA:
Để tránh Split-Brain và xây dựng HA chuẩn xác, người ta sử dụng các công cụ quản lý cụm (Cluster Managers) dựa trên thuật toán đồng thuận (Raft/Paxos) có sử dụng etcd, Consul hoặc ZooKeeper.
-
Patroni: Giải pháp HA mã nguồn mở đỉnh cao nhất cho PostgreSQL hiện nay (do Zalando phát triển). Hỗ trợ tự động Failover an toàn, chặn Split-Brain, tự động cấu hình lại
postgresql.confđồng loạt. -
PgBouncer / HAProxy: Bộ định tuyến đóng vai trò làm proxy đứng trước cụm database, tự động nhận diện IP của máy Master mới sau khi Failover để chuyển hướng traffic ứng dụng mà backend không cần sửa cấu hình kết nối.
Kiến trúc HA bằng Patroni kết hợp Streaming Replication giúp đảm bảo dịch vụ có thể chịu đựng hỏng hóc máy chủ (Hardware Failure) với thời gian gián đoạn (Downtime) chỉ từ 10 đến 30 giây để tự động thăng cấp Slave lên Master mới.
Trong hệ sinh thái của PostgreSQL, Physical Replication tuy mạnh mẽ nhưng lại gò bó vì phải sao chép 100% dữ liệu cluster. Để giải quyết bài toán chỉ sao chép một vài bảng cụ thể, sao chép giữa các phiên bản PostgreSQL khác nhau (ví dụ từ v12 lên v16 không cần downtime), hoặc đồng bộ dữ liệu đa vùng (Multi-Master), chúng ta sẽ bước sang kỹ thuật sao chép cấp độ ngữ nghĩa ở bài tiếp theo.
All rights reserved