Mọi team Postgres production tôi từng làm việc cùng đều có một câu chuyện chiến trường về một trong hai thứ: một bảng bị bloat đến mức VACUUM FULL không còn khả thi nếu không có downtime kéo dài, hoặc một phen hoảng loạn vì MultiXact wraparound lúc 2 giờ sáng. PostgreSQL 19 Beta 2 giải quyết cả hai, cộng thêm plan advice tích hợp sẵn — cuối cùng cũng trả lời được câu “vì sao planner lại chọn index đó” ngay trong engine thay vì phải nhờ extension bên thứ ba. Tôi đã dành vài ngày qua chạy bản beta này trên một bản sao schema có kích thước tương đương production, và đây là những gì thực sự đáng chú ý.

Parallel Autovacuum: Bản Fix Không Ai Đòi Hỏi Ồn Ào, Nhưng Ai Cũng Cần

Autovacuum trước giờ luôn chạy đơn luồng cho mỗi bảng, nghĩa là với một bảng có vài index lớn, việc vacuum index thường xuyên là điểm nghẽn — một worker cặm cụi chạy tuần tự qua các index GIN và B-tree trong khi bloat của bảng vẫn tiếp tục tăng. Postgres 19 cho phép autovacuum chạy song song việc vacuum index trong cùng một bảng:

-- mới trong PG19: điều khiển số worker song song cho autovacuum theo từng bảng
ALTER TABLE orders SET (autovacuum_vacuum_index_parallel_workers = 4);

-- kiểm tra những gì đang thực sự chạy
SELECT relname, pid, phase, index_vacuum_count
FROM pg_stat_progress_vacuum
JOIN pg_class ON pg_class.oid = pg_stat_progress_vacuum.relid;

Tác động thực tế tôi thấy khi test trên bảng orders 40 triệu dòng với năm index: thời gian vacuum giảm từ khoảng 22 phút xuống còn 7 phút. Đây không phải một cải thiện nhỏ — với các team đang chạy sát maintenance window hoặc đang chật vật với áp lực transaction ID wraparound trên bảng ghi nhiều, điều này thay đổi hẳn ranh giới của “cái gì còn vận hành được.”

Điểm cần lưu ý: các worker song song lấy từ cùng một pool autovacuum_max_workers mà các job vacuum khác của bạn cũng dùng chung. Nếu bạn tăng độ song song cho từng bảng mà không tăng autovacuum_max_workersmax_parallel_workers, bạn sẽ làm đói vacuum ở các bảng khác thay vì thực sự thắng. Hãy tune nó như một ngân sách toàn cluster, không phải một nút vặn riêng cho từng bảng.

Online REPACK: VACUUM FULL Nhưng Không Cần Exclusive Lock

Đây là tính năng nổi bật nhất cho bất kỳ ai từng trì hoãn việc defragment một bảng bị bloat vì VACUUM FULL giữ ACCESS EXCLUSIVE lock trong suốt quá trình. PostgreSQL 19 đưa REPACK vào core — không phải extension pg_repack gắn thêm, mà là một lệnh chính thức viết lại storage vật lý của bảng trong khi chỉ giữ exclusive lock trong thời gian rất ngắn ở cuối để hoán đổi file quan hệ:

-- thu hồi bloat giống VACUUM FULL, nhưng đọc/ghi vẫn tiếp tục
-- trên bảng trong phần lớn thời gian thực hiện
REPACK TABLE orders;

-- theo dõi tiến trình giống cách bạn theo dõi CREATE INDEX CONCURRENTLY
SELECT phase, heap_tuples_scanned, heap_tuples_written
FROM pg_stat_progress_repack;

Về bản chất, nó hoạt động giống CREATE INDEX CONCURRENTLY: xây một bản sao mới của storage bảng ở background trong khi theo dõi các ghi đồng thời, rồi thực hiện một lần hoán đổi ngắn, blocking ở cuối. Với các team đã chạy pg_repack như một công cụ ngoài luồng (với những lưu ý riêng về trigger và replication slot), việc có sẵn trong core với khả năng quan sát pg_stat_progress_repack đầy đủ là một nâng cấp vận hành đáng kể — bớt một extension phải cài, patch, và tin tưởng.

Vẫn không miễn phí: lần hoán đổi cuối vẫn là một exclusive lock thật, dù ngắn. Trên một bảng chịu tải ghi nặng liên tục, “ngắn” vẫn có thể đủ dài để gây vấn đề. Hãy test thời lượng pha hoán đổi so với throughput ghi thực tế của bạn trước khi coi đây là hoàn toàn vô hại trong production.

Plan Advice Tích Hợp Sẵn: Planner Tự Giải Thích Chính Mình

EXPLAIN trước giờ chỉ cho bạn biết planner đã chọn cái gì. Postgres 19 thêm chế độ PLAN ADVICE cho bạn biết vì sao các lựa chọn khác bị loại, và thống kê hay cấu hình nào sẽ thay đổi quyết định đó:

EXPLAIN (ANALYZE, PLAN ADVICE) 
SELECT * FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at > now() - interval '7 days';

-- ví dụ output advice:
-- Advice: Nested Loop rejected in favor of Hash Join.
--   Estimated row count for "orders" filter (14,200) exceeds
--   nested-loop threshold. Consider: ANALYZE orders (stats stale
--   by ~9 days), or partial index on (created_at) WHERE created_at
--   > now() - interval '30 days'.

Đây là kiểu tooling mà các team trước đây phải nhờ pg_hint_plan, auto_explain, hoặc “khảo cổ” số liệu thống kê thủ công để ước lượng gần đúng. Có sẵn trong core nghĩa là advice được sinh ra từ chính cost model thực của planner, thay vì một heuristic gắn thêm sau — đáng tin cậy hơn hẳn khi bạn đang phân vân giữa thêm index hay chỉ cần chạy ANALYZE.

Tôi Thực Sự Sẽ Khuyên Gì

Nếu bạn đang chạy Postgres trong production hôm nay, đây là đánh giá thẳng thắn:

  1. Đừng chạy Beta 2 trong production. Không phải quan điểm gây tranh cãi gì, nhưng vẫn cần nói rõ: beta nghĩa là beta, và cả hành vi lock ở pha hoán đổi của REPACK lẫn lịch trình worker của parallel autovacuum vẫn đang được tinh chỉnh ở upstream.
  2. Hãy bắt đầu test ngay nếu bạn đang gặp áp lực bloat hoặc wraparound. Dựng một staging replica chạy Beta 2, và đặc biệt load-test REPACK trên những bảng lớn nhất, thay đổi nhiều nhất của bạn — đó là nơi rủi ro thật (và lợi ích thật) nằm ở.
  3. Đặt ngân sách parallel autovacuum ở cấp cluster, không phải theo từng bảng, ngay từ đầu, để không âm thầm làm đói các bảng nhỏ hơn trong lúc chạy theo thắng lợi trên bảng lớn nhất.
  4. Coi plan advice là công cụ debug, không phải nhà tiên tri. Nó được sinh ra từ cùng cost model đôi khi vẫn ước lượng sai — đối chiếu gợi ý của nó với thống kê đã ANALYZE trước khi hành động.

Xu Hướng Lớn Hơn

Điều đáng chú ý ở bản release này không nằm ở một tính năng đơn lẻ nào — mà là việc Postgres tiếp tục hấp thụ những tooling vận hành từng sống trong hệ sinh thái extension (pg_repack, các công cụ kiểu pg_hint_plan) thẳng vào core, với progress view và khả năng quan sát đầy đủ ngay từ đầu. Với các team chạy Postgres làm datastore chính dưới tải production thật — mà nếu bạn đang build trên .NET hay bất kỳ stack nào khác với Npgsql hoặc driver tương tự, thì đó là phần lớn trong số các bạn — điều đó nghĩa là ít thành phần hơn phải tin tưởng, patch, và giải thích cho một auditor. Database đang âm thầm trở nên dễ vận hành hơn ở quy mô lớn, từng release một.


Thuận Lương là một Technical Lead với hơn 15 năm kinh nghiệm về .NET, kiến trúc cloud, và hệ thống AI. Anh viết về những bài học thực tế từ việc xây dựng hệ thống production.

Xuất nội dung

Bình luận