Nhiều bài trong loạt này đã nhắc VACUUM FULL như công cụ thu hồi dung lượng khi bảng bị bloat nặng. Nhưng nó là một trong những lệnh nguy hiểm nhất bạn có thể chạy trên production — không phải vì nó làm hỏng dữ liệu (nó an toàn về mặt đó), mà vì nó khóa toàn bộ bảng theo cách khiến ứng dụng "treo". Bài này đo thật ba rủi ro của VACUUM FULL, và chỉ rõ khi nào thật sự cần nó so với khi nào một lựa chọn an toàn hơn là đúng.
VACUUM FULL làm gì: viết lại toàn bảng
Khác với VACUUM thường (chỉ đánh dấu chỗ trống để tái dùng, không trả về hệ điều hành), VACUUM FULL viết lại toàn bộ bảng thành một bản sao mới gọn gàng và xóa bản cũ — nhờ đó file thu nhỏ thật và dung lượng trả về đĩa:
VACUUM FULL vf; -- 1065 MB → 355 MB (đo thật, thu nhỏ file thật)
Đây là khả năng của nó — thu hồi dung lượng mà VACUUM thường không làm được. Nhưng cách nó đạt được điều đó chính là nguồn gốc mọi rủi ro.

Hình 1: VACUUM FULL thu nhỏ file thật (khác VACUUM thường chỉ đánh dấu chỗ trống). Nhưng nó khóa ACCESS EXCLUSIVE, cần ~2× đĩa tạm, và downtime tỉ lệ kích thước — có các lựa chọn thay thế.
Rủi ro 1: khóa ACCESS EXCLUSIVE chặn cả đọc
Đây là rủi ro nghiêm trọng nhất. VACUUM FULL giữ khóa ACCESS EXCLUSIVE trên bảng — mức khóa mạnh nhất, chặn mọi thao tác, kể cả SELECT đơn giản. Đo thật: trong khi VACUUM FULL chạy trên một bảng bloat, một truy vấn đọc bình thường bị chặn hoàn toàn.

Hình 2: Đo thật, pg_locks cho thấy AccessExclusiveLock (granted=t) khi VACUUM FULL chạy; một SELECT count(*) với lock_timeout 400ms bị ERROR: canceling statement due to lock timeout. VACUUM FULL thu 1065 MB → 355 MB trong 1.198 ms. Khác VACUUM thường (không chặn đọc/ghi).
pg_lockstrong lúc VACUUM FULL:AccessExclusiveLock (granted=t)trên bảng.- Một
SELECT count(*)bình thường (vớilock_timeout 400ms) bịERROR: canceling statement due to lock timeout— nó chờ khóa mà không được.
Điều này rất khác VACUUM thường (chạy online, không chặn đọc/ghi). Với VACUUM FULL, toàn bộ ứng dụng "treo" trên bảng đó suốt thời gian chạy — trên bảng nóng, đây là downtime thật.
Rủi ro 2: cần thêm gấp đôi đĩa
Vì VACUUM FULL dựng một bản sao mới của bảng trước khi xóa bản cũ, ở đỉnh điểm nó cần chỗ cho cả hai — gần gấp đôi dung lượng bảng. Nếu đĩa đã gần đầy và bạn chạy VACUUM FULL trên một bảng lớn, nó có thể hết đĩa giữa chừng và thất bại — mà đĩa đầy thường chính là lý do bạn muốn thu hồi dung lượng ngay từ đầu. Nghịch lý này khiến VACUUM FULL đặc biệt nguy hiểm đúng lúc bạn cần nó nhất.
Rủi ro 3: downtime tỉ lệ kích thước bảng
Thời gian VACUUM FULL chạy tỉ lệ với kích thước bảng — đo thật 1.198 ms cho bảng ~1 GB, nhưng một bảng 100 GB có thể mất hàng giờ. Suốt thời gian đó, khóa ACCESS EXCLUSIVE giữ nguyên. Với bảng thật lớn trên hệ thống 24/7, thời gian khóa này là không chấp nhận được.
Khi nào dùng, và lựa chọn thay thế
| Tình huống | Nên dùng |
|---|---|
| Bloat nặng, có cửa sổ bảo trì | VACUUM FULL — đơn giản, không cần cài gì |
| Bảng 24/7, không được downtime | pg_repack — thu nhỏ online, gần như không khóa |
| Chỉ cần ngăn phình thêm | VACUUM thường / autovacuum — đủ, không thu nhỏ |
pg_repack là extension dựng lại bảng gọn online — nó tạo bản sao và đồng bộ thay đổi trong lúc chạy, chỉ cần khóa rất ngắn ở cuối để hoán đổi. Đây là công cụ đúng cho production. CLUSTER (sắp lại bảng theo index) cũng thu nhỏ nhưng cũng khóa ACCESS EXCLUSIVE như VACUUM FULL — không phải lựa chọn online.
Đánh đổi cần cân nhắc
Đừng chạy VACUUM FULL theo lịch định kỳ. Nếu bạn thấy mình cần VACUUM FULL thường xuyên, vấn đề thật là autovacuum không theo kịp (bài trước) — sửa cấu hình autovacuum để bloat không tích lũy, thay vì dọn bằng VACUUM FULL lặp đi lặp lại. VACUUM FULL nên là biện pháp một lần sau một sự cố bloat, không phải bảo trì thường quy.
Một chút bloat không đáng VACUUM FULL. Chi phí (khóa, đĩa, downtime) chỉ đáng khi bloat nghiêm trọng (ví dụ free_percent > 50% trên bảng lớn). Với bloat nhẹ, để VACUUM thường tái dùng chỗ trống là đủ — thu nhỏ file vài phần trăm không đáng downtime.
Kiểm đĩa trước khi chạy. Luôn xác nhận còn ít nhất bằng kích thước bảng dung lượng trống trước khi VACUUM FULL, để tránh hết đĩa giữa chừng. Với bảng rất lớn, pg_repack an toàn hơn vì có thể theo dõi và dừng.
Ba ý mang về
- VACUUM FULL thu nhỏ file thật nhưng khóa ACCESS EXCLUSIVE chặn cả đọc: đo thật,
pg_lockscho thấy khóa này khi chạy, và mộtSELECT count(*)bịcanceling statement due to lock timeout— khác hẳn VACUUM thường vốn không chặn đọc/ghi. - Còn hai rủi ro nữa: cần gần gấp đôi đĩa tạm (dựng bản sao trước khi xóa cũ — có thể hết đĩa giữa chừng), và downtime tỉ lệ kích thước bảng (1 GB mất ~1,2 giây, 100 GB có thể mất hàng giờ).
- Chỉ dùng khi có cửa sổ bảo trì; production 24/7 dùng pg_repack: pg_repack thu nhỏ online gần như không khóa; và nếu phải VACUUM FULL thường xuyên thì gốc rễ là autovacuum không theo kịp — sửa cấu hình chứ đừng dọn lặp lại.
Phần sau ta quay lại một tối ưu chống bloat đã nhắc nhiều lần: Phần sau mổ xẻ HOT update — cách PostgreSQL cập nhật một dòng mà không đụng index, điều kiện để nó xảy ra, và vì sao nó giảm mạnh bloat.