Bài về VACUUM FULL cho thấy nó thu hồi dung lượng bloat nhưng khóa toàn bảng — chặn cả đọc lẫn ghi suốt thời gian chạy, không dùng được trên hệ thống 24/7. Nhiều bài đã nhắc pg_repack là giải pháp thay thế. Bài này đo thật nó: pg_repack làm đúng công việc của VACUUM FULL (thu nhỏ bảng về kích thước thật) nhưng online — trong khi nó chạy, ứng dụng vẫn đọc và ghi bình thường. Đây là công cụ chuẩn để dọn bloat nặng trên production không được downtime.
Cài đặt và cách dùng
pg_repack là một extension cộng với một chương trình dòng lệnh (khác các extension chỉ-SQL) — bạn cài cả hai:
# cài gói (tên tùy phiên bản PostgreSQL)
apt-get install postgresql-16-repack
CREATE EXTENSION pg_repack;
# chạy như chương trình, KHÔNG phải lệnh SQL
pg_repack -d ten_db -t ten_bang
Đo thật trên container này: pg_repack 1.5.3, repack một bảng bloat rp từ 381 MB xuống 95 MB (500.000 dòng sống giữ nguyên), mất chưa tới nửa giây.

Hình 1: pg_repack là extension cộng chương trình dòng lệnh. Cơ chế năm bước: bản sao + trigger đồng bộ + hoán đổi khóa ngắn — khác VACUUM FULL vốn khóa suốt thời gian chạy.
Đo thật: đọc không bị chặn khi repack chạy
Đây là điểm khác biệt then chốt. Bài trước đã đo VACUUM FULL chặn một SELECT count(*) bình thường (nó bị canceling statement due to lock timeout). Giờ đo cùng kịch bản với pg_repack: repack một bảng bloat 1894 MB, trong lúc đó chạy một SELECT đồng thời:

Hình 2: pg_repack thu nhỏ rp 381→95 MB và rp2 1894→631 MB. Trong lúc repack rp2, một SELECT count(*) đồng thời trả về 2.666.666 dòng — thành công, khác hẳn VACUUM FULL vốn khiến SELECT bị canceling statement due to lock timeout.
VACUUM FULL(bài trước): SELECT đồng thời bị hủy —ERROR: canceling statement due to lock timeout.pg_repack: SELECT đồng thời trả về 2.666.666 dòng — thành công, không bị chặn.
Trong lúc pg_repack copy dữ liệu (phần lâu nhất của quá trình), đọc và ghi chạy bình thường. Chỉ bước hoán đổi cuối cùng cần khóa ACCESS EXCLUSIVE, và chỉ vài mili giây.
Cơ chế năm bước
pg_repack đạt được tính online bằng cách không sửa bảng gốc mà dựng một bản sao gọn rồi hoán đổi:
- Tạo một bảng bản sao rỗng và một trigger trên bảng gốc để ghi lại mọi thay đổi (INSERT/UPDATE/DELETE) xảy ra trong lúc repack.
- COPY dữ liệu sống từ bảng gốc sang bản sao — đây là phần lâu nhất, nhưng không khóa đọc/ghi.
- Replay các thay đổi đã được trigger ghi lại (để bản sao bắt kịp bảng gốc).
- Dựng lại index trên bản sao.
- Khóa ACCESS EXCLUSIVE rất ngắn để hoán đổi (rename) hai bảng — chỉ bước này khóa, và chỉ vài mili giây.
Kết quả: bảng gốc được thay bằng bản sao gọn, không còn bloat, mà ứng dụng gần như không cảm nhận được gián đoạn.
Đánh đổi cần cân nhắc
pg_repack cần thêm ~2x đĩa như VACUUM FULL. Nó dựng một bản sao đầy đủ của bảng trước khi hoán đổi, nên ở đỉnh điểm cần chỗ cho cả bảng gốc và bản sao. Đây là ràng buộc chung với mọi cách "viết lại bảng" — kiểm dung lượng trống trước khi chạy trên bảng lớn.
Cần khóa ngắn ở đầu và cuối — không hoàn toàn "không khóa". pg_repack lấy một khóa ngắn lúc tạo trigger (đầu) và lúc hoán đổi (cuối). Nếu có giao dịch dài đang chạy chiếm khóa bảng, pg_repack có thể phải chờ hoặc bị chặn ở hai điểm này. "Gần như không khóa" chứ không phải "không khóa tuyệt đối".
Là extension bên thứ ba, cần cài và cấp quyền. Khác VACUUM FULL (lệnh SQL sẵn có), pg_repack phải cài binary + extension, và thường cần quyền cao. Trên dịch vụ quản lý (RDS, Cloud SQL), kiểm xem nó có được hỗ trợ không — nhiều dịch vụ có sẵn nhưng một số không. Với hệ thống tự vận hành, đây là công cụ đáng cài sẵn cho bảo trì bloat.
Ba ý mang về
- pg_repack thu nhỏ bảng bloat như VACUUM FULL nhưng online: đo thật, repack bảng 1894 MB xuống 631 MB trong khi một
SELECTđồng thời vẫn trả về 2,67 triệu dòng — khác hẳnVACUUM FULLvốn khiến SELECT bị hủy vì lock timeout. - Cơ chế là bản sao + trigger đồng bộ + hoán đổi khóa ngắn: pg_repack copy dữ liệu sang bảng bản sao mới (không khóa), replay thay đổi qua trigger, rồi chỉ khóa ACCESS EXCLUSIVE vài mili giây ở bước hoán đổi cuối cùng.
- Là lựa chọn cho production 24/7 nhưng có ràng buộc: cần cài binary + extension, thêm ~2x đĩa tạm, và có hai điểm khóa ngắn ở đầu/cuối — "gần như không khóa" chứ không tuyệt đối; kiểm hỗ trợ trên dịch vụ quản lý.
Phần sau ta chuyển sang chủ đề cấu hình bộ nhớ nền tảng: Phần sau mổ xẻ shared_buffers — bộ nhớ đệm chính của PostgreSQL, nên đặt bao nhiêu, và vì sao "càng nhiều càng tốt" không đúng.