Nỗi lo về các bảng dữ liệu khổng lồ và nhu cầu tự động hóa
Việc quản lý một cơ sở dữ liệu tăng trưởng hàng triệu hàng mỗi ngày sớm muộn gì cũng sẽ dẫn đến rào cản về hiệu năng. Tôi nhớ có một dự án mà bảng ‘logs’ duy nhất đã lên tới 500GB. Các truy vấn SELECT đơn giản bắt đầu mất hàng phút để xử lý, và các tác vụ bảo trì như VACUUM về cơ bản là không thể hoàn thành. Đã từng làm việc với MySQL, PostgreSQL và MongoDB trong nhiều dự án khác nhau, mỗi hệ quản trị đều có thế mạnh riêng, nhưng tính năng partitioning (phân vùng) gốc của PostgreSQL thực sự tỏa sáng khi xử lý dữ liệu có cấu trúc ở quy mô lớn.
PostgreSQL đã giới thiệu tính năng native declarative partitioning (phân vùng khai báo gốc) từ phiên bản 10, đây là một bước tiến lớn. Tuy nhiên, có một vấn đề: nó không tự động quản lý các phân vùng cho bạn. Bạn phải tự tay tạo bảng cho tháng tiếp theo, quản lý các ràng buộc (constraints) và tự xóa dữ liệu cũ.
Nếu bạn quên tạo phân vùng tiếp theo, ứng dụng sẽ bắt đầu báo lỗi vì không có nơi nào để lưu trữ dữ liệu mới. Đây chính là lúc pg_partman trở thành “cứu cánh”. Nó đóng vai trò là công cụ tự động hóa xử lý vòng đời của các phân vùng, giúp bạn có thể yên tâm kê gối ngủ ngon.
Cài đặt pg_partman vào môi trường của bạn
Trước khi có thể tự động hóa bất cứ điều gì, chúng ta cần cài đặt extension này trên máy chủ. Nếu bạn đang sử dụng các dịch vụ được quản lý (managed services) như AWS RDS hoặc Azure Database for PostgreSQL, pg_partman thường đã có sẵn và bạn chỉ cần kích hoạt nó. Đối với những người tự quản lý máy chủ Linux, bạn sẽ cần cài đặt gói tương ứng với phiên bản Postgres đang dùng.
Trên hệ thống Debian/Ubuntu cho PostgreSQL 16, quá trình cài đặt như sau:
sudo apt-get update
sudo apt-get install postgresql-16-partman
Sau khi cài đặt xong gói, bạn cần yêu cầu PostgreSQL tải nó lên. Điều này đòi hỏi thay đổi trong tệp postgresql.conf. Hãy tìm cài đặt shared_preload_libraries và thêm pg_partman_bgw (Background Worker). Điều này cho phép extension chạy các tác vụ bảo trì tự động dưới nền.
# Chỉnh sửa tệp postgresql.conf
shared_preload_libraries = 'pg_partman_bgw'
Khởi động lại dịch vụ PostgreSQL để áp dụng các thay đổi. Sau khi khởi động lại, hãy đăng nhập vào cơ sở dữ liệu và tạo extension cùng một schema riêng cho nó. Tôi thực sự khuyên bạn nên để pg_partman trong schema riêng để giữ cho schema public luôn gọn gàng.
CREATE SCHEMA partman;
CREATE EXTENSION pg_partman SCHEMA partman;
Cấu hình phân vùng tự động đầu tiên
Hãy xem xét một ví dụ thực tế. Giả sử chúng ta có bảng iot_sensor_data nhận hàng nghìn bản ghi mỗi giây. Chúng ta muốn phân vùng bảng này theo cột thời gian created_at, tạo một phân vùng mới cho mỗi ngày.
Đầu tiên, hãy tạo bảng “mẹ” (parent table). Lưu ý rằng trong declarative partitioning, bảng mẹ định nghĩa cấu trúc và khóa phân vùng (partition key).
CREATE TABLE public.iot_sensor_data (
id bigint NOT NULL,
sensor_id int NOT NULL,
data_value numeric,
created_at timestamptz NOT NULL DEFAULT now()
) PARTITION BY RANGE (created_at);
Bây giờ, thay vì chạy thủ công lệnh CREATE TABLE ... PARTITION OF, chúng ta hãy để pg_partman tiếp quản. Chúng ta gọi hàm create_parent. Hàm này thông báo cho extension bắt đầu quản lý bảng của chúng ta, sử dụng khoảng thời gian là ‘daily’ (hàng ngày) và tạo trước 4 phân vùng để không bao giờ bị thiếu hụt không gian lưu trữ.
SELECT partman.create_parent(
p_parent_table := 'public.iot_sensor_data',
p_control := 'created_at',
p_type := 'native',
p_interval := 'daily',
p_premake := 4
);
Tham số p_premake rất quan trọng. Nó đảm bảo rằng nếu hôm nay là Thứ Hai, các phân vùng cho Thứ Ba, Thứ Tư, Thứ Năm và Thứ Sáu đã tồn tại sẵn. “Vùng đệm” này bảo vệ bạn khỏi những lỗi tạm thời trong các tác vụ bảo trì.
Tự động hóa tác vụ bảo trì
Mặc dù chúng ta đã thiết lập cách thức phân vùng cho pg_partman, nó vẫn cần một trình kích hoạt để thực sự tạo ra các phân vùng mới theo thời gian. Có hai cách chính để thực hiện việc này: sử dụng Background Worker (BGW) hoặc sử dụng cron job.
Cách 1: Background Worker (Khuyên dùng)
Vì chúng ta đã thêm pg_partman_bgw vào shared_preload_libraries trước đó, chúng ta chỉ cần cấu hình nó trong postgresql.conf. Đây là phương pháp ổn định nhất vì nó không phụ thuộc vào các công cụ bên ngoài.
# Thêm các dòng này vào postgresql.conf
pg_partman_bgw.interval = 3600 # Chạy mỗi giờ một lần
pg_partman_bgw.role = 'postgres'
pg_partman_bgw.dbname = 'ten_database_cua_ban'
Cách 2: Sử dụng pg_cron
Nếu bạn muốn kiểm soát chi tiết hơn hoặc đã sử dụng pg_cron cho các tác vụ khác, bạn có thể lập lịch hàm bảo trì theo cách thủ công:
SELECT cron.schedule('0 * * * *', $$SELECT partman.run_maintenance()$$);
Mỗi khi run_maintenance() thực thi, nó sẽ kiểm tra tất cả các bảng được quản lý bởi pg_partman, tạo các phân vùng mới nếu cần dựa trên giá trị p_premake và xử lý việc lưu trữ dữ liệu.
Lưu trữ và dọn dẹp dữ liệu
Một trong những ưu điểm lớn nhất của partitioning là khả năng xóa dữ liệu cũ ngay lập tức. Việc xóa 100 triệu hàng bằng lệnh DELETE sẽ tạo ra một lượng lớn log WAL và gây ra hiện tượng phân mảnh (bloat) bảng. Với partitioning, bạn chỉ cần drop (xóa) toàn bộ bảng phân vùng, đây là một thao tác có độ phức tạp O(1).
Để tự động hóa việc này, hãy cập nhật bảng part_config cho bảng mẹ cụ thể. Giả sử chúng ta chỉ muốn giữ lại 30 ngày dữ liệu cảm biến.
UPDATE partman.part_config
SET retention = '30 days',
retention_keep_table = false
WHERE parent_table = 'public.iot_sensor_data';
Với retention_keep_table = false, pg_partman sẽ thực sự xóa các bảng phân vùng cũ. Nếu bạn đặt thành true, nó sẽ chỉ gỡ liên kết chúng khỏi bảng mẹ nhưng vẫn để lại dữ liệu trên đĩa — hữu ích nếu bạn muốn lưu trữ (archive) chúng vào các kho lưu trữ lạnh như S3 trước khi xóa hẳn.
Xác minh và kiểm tra tình trạng hệ thống
Khi mọi thứ đã hoạt động, bạn không nên mặc định rằng nó sẽ luôn chạy tốt. Tôi luôn kiểm tra part_config và danh sách các bảng thực tế để đảm bảo quá trình tự động hóa diễn ra suê sẻ.
Để xem tất cả các bảng hiện đang được quản lý bởi extension:
SELECT parent_table, partition_type, partition_interval, retention
FROM partman.part_config;
Để xác minh rằng các phân vùng thực sự được tạo trong cơ sở dữ liệu, hãy sử dụng lệnh \d+ trong psql trên bảng mẹ. Bạn sẽ thấy danh sách các phân vùng con được ánh xạ tới các khoảng thời gian cụ thể.
Nếu bạn gặp tình huống tác vụ bảo trì không chạy và bị thiếu phân vùng, bạn có thể ép chạy thủ công cho một bảng cụ thể để bắt kịp tiến độ:
SELECT partman.run_maintenance('public.iot_sensor_data');
Lời kết
Chuyển đổi từ một bảng đơn khối (monolithic) duy nhất sang thiết lập phân vùng tự động là một trong những thay đổi có tác động lớn nhất mà bạn có thể thực hiện cho một cơ sở dữ liệu có tốc độ tăng trưởng cao. Nó giải quyết vấn đề suy giảm hiệu năng và cung cấp một cách sạch sẽ để quản lý việc lưu trữ dữ liệu mà không tốn tài nguyên cho các thao tác DELETE nặng nề. Bằng cách kết hợp tính năng partitioning gốc của PostgreSQL với pg_partman, bạn sẽ có một hệ thống có thể mở rộng lên tới hàng tỷ hàng trong khi hầu như không cần can thiệp thủ công sau khi các bước thiết lập ban đầu đã hoàn tất.

