Khi ổ cứng vẫn đầy dù bạn đã xóa hàng triệu bản ghi
Hãy tưởng tượng kịch bản này: Database báo sắp cạn bộ nhớ. Bạn nhanh chóng chạy lệnh DELETE để xóa 200GB dữ liệu log cũ. Thế nhưng, dung lượng đĩa cứng vẫn đứng im ở con số 500GB như chưa hề có cuộc chia ly. Mình từng gặp tình huống này khi quản lý hệ thống giao dịch lớn. Dữ liệu thực tế chỉ còn 300GB, nhưng OS vẫn báo chiếm dụng 500GB. Đây chính là hiện tượng Bloat (phình to dữ liệu) kinh điển trong PostgreSQL.
Phản xạ đầu tiên của nhiều người là chạy VACUUM FULL. Đừng làm vậy trên môi trường Production! Lệnh này sẽ chiếm quyền khóa bảng hoàn toàn (Access Exclusive Lock). Toàn bộ ứng dụng của bạn sẽ bị treo, mọi truy vấn đọc/ghi đều phải xếp hàng chờ đợi. Với bảng dữ liệu lớn, quá trình này có thể kéo dài hàng giờ đồng hồ.
Để xử lý triệt để mà không làm gián đoạn dịch vụ, pg_repack là công cụ cứu cánh mà mọi kỹ sư Database cần thành thạo.
Tại sao PostgreSQL lại bị Bloat?
Gốc rễ vấn đề nằm ở cơ chế MVCC (Multi-Version Concurrency Control). Khi bạn UPDATE, Postgres không sửa đè lên dòng cũ. Nó đánh dấu dòng cũ là “hết hạn” (dead tuple) và chèn một dòng mới. Lệnh DELETE cũng tương tự, chỉ đánh dấu là đã xóa chứ không thu hồi không gian đĩa ngay lập tức.
Tiến trình Autovacuum sẽ dọn dẹp các dead tuple này. Tuy nhiên, nó chỉ cho phép Postgres tái sử dụng không gian đó cho dữ liệu mới. Nó không trả lại dung lượng cho Hệ điều hành. Nếu bạn xóa một lượng lớn dữ liệu đột ngột, hoặc bảng có tần suất update cực cao, file dữ liệu sẽ phình to không kiểm soát.
So sánh các giải pháp truyền thống
Trước khi dùng pg_repack, hãy nhìn lại những hạn chế của các phương pháp quen thuộc:
- VACUUM: Chỉ dọn dẹp nội bộ bảng. Dung lượng đĩa không giảm.
- VACUUM FULL: Thu hồi đĩa hiệu quả nhưng gây khóa bảng (Downtime).
- CLUSTER: Sắp xếp lại dữ liệu theo index, cũng gây khóa bảng tương tự
VACUUM FULL.
pg_repack hoạt động như thế nào?
pg_repack cho phép tái cấu trúc bảng và index mà không cần khóa bảng lâu dài. Bạn vẫn có thể SELECT, INSERT, UPDATE, DELETE bình thường trong khi nó đang làm việc.
Quy trình xử lý gồm 5 bước:
- Tạo một bảng tạm (shadow table) chứa toàn bộ dữ liệu từ bảng gốc.
- Thiết lập trigger trên bảng gốc để ghi lại mọi thay đổi phát sinh vào một bảng log.
- Xây dựng lại các index trên bảng tạm mới.
- Đẩy toàn bộ thay đổi từ bảng log vào bảng tạm để đồng bộ dữ liệu.
- Hoán đổi (swap) tên bảng trong system catalog và xóa bảng cũ. Bước này chỉ diễn ra trong vài mili giây.
Hướng dẫn cài đặt và sử dụng thực tế
Bạn cần cài đặt pg_repack ở cả mức hệ điều hành và kích hoạt extension trong database.
1. Cài đặt công cụ
Với PostgreSQL 15 trên Ubuntu, hãy dùng lệnh:
sudo apt-get update
sudo apt-get install postgresql-15-repack
Tiếp theo, hãy đăng nhập vào database và kích hoạt extension:
CREATE EXTENSION pg_repack;
2. Kiểm tra mức độ Bloat
Đừng vội vàng repack mọi thứ. Hãy ưu tiên các bảng có tỷ lệ bloat trên 20%. Bạn có thể dùng script pg_bloat_check để quét toàn bộ hệ thống. Nếu một bảng 100GB nhưng bloat chiếm 40GB, đó là lúc cần hành động.
3. Thực thi lệnh từ Terminal
Lưu ý: Bạn chạy pg_repack từ dòng lệnh, không phải trong psql.
Repack một bảng cụ thể:
pg_repack -h localhost -U postgres -d shop_db -t orders
Chỉ tối ưu hóa Index (tiết kiệm thời gian hơn):
pg_repack -h localhost -U postgres -d shop_db -t orders --only-indexes
Chạy thử (Dry run) để kiểm tra:
pg_repack -h localhost -U postgres -d shop_db -t orders --dry-run
Lưu ý quan trọng khi triển khai thực tế
Qua nhiều lần tối ưu các bảng quy mô TB, mình rút ra 4 lưu ý quan trọng:
- Dung lượng đĩa trống: Bạn cần ít nhất khoảng trống bằng kích thước bảng gốc + index. Nếu bảng nặng 300GB và ổ cứng còn dưới 350GB, đừng chạy repack vì sẽ gây tràn đĩa.
- Tài nguyên I/O: Quá trình copy dữ liệu ngốn rất nhiều I/O. Hãy thực hiện vào giờ thấp điểm (ví dụ 2h sáng) để tránh ảnh hưởng đến trải nghiệm người dùng.
- Yêu cầu Primary Key:
pg_repackbắt buộc bảng phải có Primary Key hoặc Unique Index (Not Null). Nếu không, công cụ này sẽ không hoạt động. - Transaction dài: Nếu có một câu query khác đang chạy quá lâu trên bảng đó,
pg_repacksẽ không thể thực hiện bước swap cuối cùng.
Lời kết
Quản lý Bloat là nhiệm vụ bắt buộc khi vận hành PostgreSQL quy mô lớn. pg_repack giúp bạn lấy lại dung lượng đĩa, tăng tốc độ truy vấn và giữ cho hệ thống luôn sẵn sàng 24/7. Hãy luôn thử nghiệm trên môi trường Staging trước khi áp dụng vào Production để kiểm soát tốt nhất các rủi ro về tài nguyên.
