Phân vùng theo thời gian (ngày/tháng) không phải dựng xong là quên. Nó cần một vòng đời vận hành: tạo partition mới trước khi dữ liệu tới, dọn partition cũ đi, và có lưới an toàn cho dữ liệu ngoài dải. Bài này đo thật các thao tác quản lý — đặc biệt lợi ích lớn nhất của phân vùng theo thời gian: dọn dữ liệu cũ bằng DROP thay vì DELETE.
Tạo partition tương lai và DEFAULT partition
Với bảng phân vùng theo tháng, bạn phải tạo partition cho các tháng sắp tới trước khi dữ liệu của tháng đó tới. Quên tạo thì INSERT dữ liệu tháng mới sẽ lỗi — không partition nào nhận. Giải pháp:
-- Tạo trước partition tương lai (thủ công, cron job, hoặc pg_partman tự động):
CREATE TABLE log_2025_12 PARTITION OF log_pt FOR VALUES FROM ('2025-12-01') TO ('2026-01-01');
-- Lưới an toàn: DEFAULT partition nhận mọi dòng không khớp vùng nào:
CREATE TABLE log_default PARTITION OF log_pt DEFAULT;
DEFAULT partition bắt các dòng ngoài dải đã khai. Đo thật: chèn một dòng ngày 2026-05-15 (ngoài mọi vùng tháng đã tạo) — nó rơi vào log_default thay vì báo lỗi. Nhưng dữ liệu kẹt ở default là dấu hiệu thiếu partition đúng; cần giám sát và dọn.

Hình 1: Vòng đời partition theo thời gian — tạo partition tương lai trước, DEFAULT partition làm lưới an toàn, dọn dữ liệu cũ bằng DROP (thay DELETE), DETACH/ATTACH để tách/gắn partition.
Đo thật: DROP partition thay vì DELETE
Đây là lợi ích lớn nhất của phân vùng theo thời gian. Để dọn dữ liệu cũ (ví dụ xóa toàn bộ log tháng 10), so hai cách trên ~303 nghìn dòng:

Hình 2: DEFAULT partition bắt dòng ngoài dải (2026-05 → log_default). Dọn 1 tháng: DROP TABLE xóa 303k dòng trong 0,069 s với 0 dead tuple; DELETE tương đương mất 0,118 s và để lại 303.272 dead tuple (cần VACUUM). DETACH tách partition thành bảng độc lập trong 0,043 s, dữ liệu còn nguyên.
- DROP partition:
DROP TABLE log_2025_10xóa cả 303k dòng trong 0,069 giây với 0 dead tuple. Nó không quét dòng — chỉ xóa file của partition. - DELETE trên bảng phẳng:
DELETE ... WHERE ngay < '2025-11-01'mất 0,118 giây và để lại 303.272 dead tuple, cần VACUUM để đòi lại không gian.
Ở quy mô 303k dòng này, chênh lệch thời gian còn nhỏ. Nhưng khác biệt quan trọng là dead tuple: DROP để lại 0, DELETE để lại hàng trăm nghìn — mà dead tuple nghĩa là bảng bloat (không co lại nếu không VACUUM FULL), cộng với WAL khổng lồ mà DELETE sinh ra (mỗi dòng một bản ghi). Trên bảng thật hàng chục triệu dòng, DELETE có thể mất hàng phút và cần VACUUM nặng, còn DROP vẫn gần như tức thì. Đây chính là lý do vận hành số một để phân vùng dữ liệu theo thời gian.
DETACH và ATTACH
Đôi khi bạn muốn tách partition ra (để archive sang lưu trữ lạnh, pg_dump riêng) thay vì xóa thẳng:
ALTER TABLE log_pt DETACH PARTITION log_2025_11; -- tách thành bảng độc lập
ALTER TABLE log_pt ATTACH PARTITION bang_co_san -- gắn bảng dựng sẵn làm partition
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
Đo thật: DETACH mất 0,043 giây, biến log_2025_11 thành một bảng độc lập với 293.486 dòng còn nguyên — không xóa dữ liệu. Từ đó bạn có thể archive rồi DROP, hoặc giữ lại. ATTACH làm ngược lại: gắn một bảng đã dựng sẵn (và nạp dữ liệu) làm partition mới, hữu ích để chuẩn bị dữ liệu ngoài giờ cao điểm rồi gắn vào.
Đánh đổi cần cân nhắc
Tự động hóa tạo partition là bắt buộc trên production. Đừng dựa vào việc nhớ tạo partition tháng sau bằng tay — quên một lần là INSERT lỗi hàng loạt. Dùng pg_partman (extension chuyên quản lý partition theo thời gian, tự tạo tương lai và dọn cũ), hoặc một cron job chạy CREATE TABLE ... PARTITION OF định kỳ. DEFAULT partition là lưới an toàn, không phải giải pháp thay thế.
DETACH thường (không CONCURRENTLY) lấy khóa mạnh. ALTER TABLE ... DETACH PARTITION mặc định lấy ACCESS EXCLUSIVE trên bảng cha một thoáng — có thể chặn truy vấn. PostgreSQL 14+ có DETACH PARTITION ... CONCURRENTLY để tách mà không khóa dài; dùng nó trên hệ thống bận.
ATTACH kiểm tra ràng buộc có thể tốn thời gian. Khi ATTACH một bảng, PostgreSQL phải kiểm mọi dòng có nằm đúng dải partition không (trừ khi có sẵn CHECK constraint khớp). Trên bảng lớn, việc quét này chậm — thêm CHECK constraint trước để PostgreSQL bỏ qua bước quét.
Ba ý mang về
- Dọn dữ liệu cũ bằng DROP partition, không phải DELETE: đo thật, DROP xóa 303k dòng trong 0,069 giây với 0 dead tuple (chỉ xóa file), còn DELETE tương đương mất 0,118 giây và để lại 303.272 dead tuple cần VACUUM — khác biệt càng lớn khi bảng càng to (DELETE sinh WAL khổng lồ + bloat).
- Tạo partition tương lai trước, DEFAULT partition làm lưới an toàn: quên tạo partition tháng mới thì INSERT lỗi; DEFAULT bắt dòng ngoài dải (đo thật: dòng 2026-05 rơi vào log_default) — nhưng tự động hóa (pg_partman/cron) là bắt buộc, DEFAULT không thay thế.
- DETACH tách partition thành bảng độc lập (0,043 giây, dữ liệu còn nguyên) để archive trước khi DROP; dùng DETACH CONCURRENTLY (PG14+) để tránh khóa dài, và thêm CHECK constraint trước khi ATTACH bảng lớn.
Phần sau ta xét chi tiết kỹ thuật quan trọng khi phân vùng: Phần sau mổ xẻ index trên bảng phân vùng — index tạo trên bảng cha lan xuống mọi partition thế nào, unique index và ràng buộc khóa phân vùng, và CREATE INDEX không khóa toàn bảng.