PostgreSQL Logical Replication: Đồng bộ dữ liệu chọn lọc không gây gián đoạn (Đánh giá 6 tháng vận hành thực tế)

Database tutorial - IT technology blog
Database tutorial - IT technology blog

Mở rộng quy mô vượt ra khỏi cấu trúc Monolith

Sáu tháng trước, cơ sở dữ liệu chính 800GB của chúng tôi đã chạm ngưỡng giới hạn. Nó phải gánh vác mọi thứ cùng lúc: xử lý giao dịch thanh toán, quản lý phiên làm việc của người dùng và chạy các truy vấn join phân tích khổng lồ cho đội ngũ marketing.

Những truy vấn marketing đó—thường join hơn 15 bảng—khiến mức sử dụng CPU tăng vọt lên 95%, gây ra lỗi timeout API ngẫu nhiên cho khách hàng thực tế. Chúng tôi cần chuyển dữ liệu báo cáo sang một instance riêng biệt. Tuy nhiên, có hai yêu cầu không thể thỏa hiệp: chúng tôi phải loại bỏ thông tin cá nhân nhạy cảm (PII) và các bảng log nặng nề, đồng thời không được phép để hệ thống ngừng hoạt động dù chỉ một phút.

Physical streaming replication là tiêu chuẩn cho tính sẵn sàng cao (high availability), nhưng nó là một công cụ khá “thô”. Nó tạo ra một bản sao chính xác từng byte của toàn bộ instance, nghĩa là bạn không thể bỏ qua bảng lưu trữ (archive table) 2TB từ năm 2019. Đây chính là lúc logical replication cứu nguy cho chúng tôi. Nó cho phép chúng tôi stream các bảng cụ thể sang một database hoàn toàn khác, thậm chí chạy trên một instance giá rẻ chỉ 40 USD/tháng. Sau nửa năm vận hành thực tế, tôi đã đúc kết được những rủi ro mà các tài liệu hướng dẫn tiêu chuẩn thường bỏ qua.

Các khái niệm cốt lõi: Publications và Subscriptions

Logical replication sử dụng mô hình publish-subscribe (xuất bản-đăng ký) đơn giản. Thay vì gửi các tệp Write-Ahead Log (WAL) thô, Postgres giải mã các bản ghi đó thành những thay đổi cụ thể ở cấp dòng như INSERT, UPDATE và DELETE.

  • Publisher: Cơ sở dữ liệu nguồn chính của bạn. Bạn định nghĩa một “Publication”, đơn giản là danh sách các bảng bạn muốn chia sẻ.
  • Subscriber: Cơ sở dữ liệu đích. Nó tạo ra một “Subscription” kết nối tới publisher để lấy dữ liệu.
  • Logical Decoding: Công cụ trích xuất các thay đổi từ WAL. Để kích hoạt tính năng này, bạn phải thiết lập wal_level thành logical.

Một ưu điểm lớn đối với chúng tôi là database subscriber vẫn hoàn toàn có thể ghi dữ liệu (writable). Chúng tôi đã thêm các index bổ sung trên subscriber dành riêng cho việc báo cáo mà không làm chậm database chính. Thậm chí, bạn có thể tổng hợp dữ liệu từ ba microservice khác nhau vào một trung tâm báo cáo duy nhất. Nó được thiết kế để mang lại sự linh hoạt.

Thực hành: Thiết lập luồng dữ liệu đầu tiên

Đừng chạm vào console của môi trường production cho đến khi bạn đã xác minh đường truyền mạng giữa các server. Dưới đây là quy trình chính xác mà chúng tôi đã sử dụng cho việc di chuyển dữ liệu.

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

Đầu tiên, hãy điều chỉnh tệp postgresql.conf. Việc thay đổi wal_level yêu cầu phải khởi động lại service, vì vậy hãy lên lịch thực hiện vào khung giờ ít lưu lượng truy cập. Chúng tôi đã thực hiện vào lúc 3 giờ sáng thứ Ba.

# Chỉnh sửa tệp postgresql.conf
wal_level = logical
max_replication_slots = 10
max_wal_senders = 10

Sau khi service hoạt động trở lại, hãy cập nhật pg_hba.conf để cho phép subscriber kết nối. Sử dụng một user riêng biệt với quyền tối thiểu để giữ cho bề mặt tấn công ở mức nhỏ nhất.

-- Tạo người dùng replication riêng biệt
CREATE ROLE replication_user WITH REPLICATION LOGIN PASSWORD 'your_secure_password';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO replication_user;

Bước 2: Tạo Publication

Tiếp theo, hãy cho database nguồn biết những bảng nào đã sẵn sàng để đồng bộ. Nếu bạn đang nạp dữ liệu vào database đích bằng dữ liệu ngoại vi trước, tôi khuyên bạn nên sử dụng toolcraft.app/vi/tools/data/csv-to-json. Nó chạy hoàn toàn trên trình duyệt của bạn, vì vậy không có dữ liệu nhạy cảm nào rời khỏi máy của bạn—hoàn hảo cho việc chuẩn bị schema nhanh chóng.

Trên database publisher:

-- Tạo publication cho các bảng cụ thể
CREATE PUBLICATION my_reporting_pub FOR TABLE orders, customers;

Bước 3: Chuẩn bị Subscriber

Subscriber cần cấu trúc bảng sẵn sàng trước khi dữ liệu bắt đầu đổ về. Logical replication không đồng bộ các thay đổi DDL như CREATE TABLE. Hãy sử dụng pg_dump để chỉ lấy schema mà không kèm dữ liệu.

# Chỉ xuất schema từ publisher
pg_dump -h publisher_host -U postgres -s my_db > schema.sql

# Nhập schema vào subscriber
psql -h subscriber_host -U postgres my_reporting_db < schema.sql

Bước 4: Tạo Subscription

Bước cuối cùng là kết nối hai bên. Ngay khi bạn chạy lệnh này, subscriber sẽ kích hoạt giai đoạn snapshot để sao chép các dòng hiện có trước khi chuyển sang chế độ streaming thời gian thực.

-- Trên database subscriber
CREATE SUBSCRIPTION my_reporting_sub 
CONNECTION 'host=publisher_host dbname=my_db user=replication_user password=your_secure_password' 
PUBLICATION my_reporting_pub;

Những gì tôi học được sau 6 tháng vận hành thực tế

Việc thiết lập ban đầu khá dễ dàng, nhưng bảo trì mới là nơi thử thách thực sự. Chúng tôi đã rút ra được ba bài học xương máu trong suốt 180 ngày vận hành.

1. Bẫy thay đổi Schema

Nếu bạn thêm một cột vào publisher nhưng quên cập nhật trên subscriber, việc replication sẽ dừng lại ngay lập tức. Tệ hơn nữa, các tệp log WAL trên publisher sẽ tăng trưởng vô hạn trong khi subscriber bị kẹt. Hiện tại, chúng tôi luôn áp dụng thay đổi DDL cho subscriber trước. Điều này đảm bảo bên đích luôn sẵn sàng nhận định dạng dữ liệu mới trước khi publisher bắt đầu gửi nó.

2. Giám sát các Replication Slot

Replication slot là con dao hai lưỡi. Nếu subscriber ngoại tuyến trong vài giờ, publisher sẽ đệm (buffer) tất cả các thay đổi trên đĩa. Trong một sự cố gián đoạn mạng ở tháng thứ tư, dung lượng đĩa của publisher đã tăng vọt 120GB chỉ trong hai giờ. Bây giờ, chúng tôi sử dụng cảnh báo định kỳ 15 phút trên pg_replication_slots để phát hiện các luồng bị trì trệ trước khi đĩa đầy.

SELECT slot_name, active, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS delay_bytes
FROM pg_replication_slots;

3. Vấn đề về Sequence

Các Sequence không được replication. Nếu bạn sử dụng các cột SERIAL hoặc IDENTITY, giá trị sequence của subscriber sẽ vẫn ở mức 1. Đây không phải là vấn đề đối với việc báo cáo chỉ đọc (read-only), nhưng sẽ là một thảm họa nếu bạn cố gắng failover. Bạn phải đồng bộ các giá trị sequence một cách thủ công nếu có kế hoạch ghi dữ liệu vào database subscriber.

Lời kết

Việc giảm được 40% tải đã nói lên tất cả. Bằng cách tách biệt các truy vấn phức tạp, độ trễ API chính của chúng tôi đã giảm trung bình 60ms. Logical replication không phải là một công cụ “thiết lập rồi quên đi”—nó đòi hỏi sự giám sát chủ động và một quy trình di chuyển schema kỷ luật. Nhưng đối với việc đồng bộ dữ liệu chọn lọc, đây là công cụ đáng tin cậy nhất trong hệ sinh thái Postgres. Nếu hệ thống Monolith của bạn đang gồng mình dưới sức nặng của các truy vấn phân tích, đã đến lúc phải chia sẻ bớt gánh nặng đó.

Share: