Bài trước đo thấy phần lớn WAL "phình to" đến từ full-page image (FPI) — lần chạm trang đầu tiên sau mỗi checkpoint chép nguyên cả trang 8KB vào WAL. Câu hỏi tự nhiên tiếp theo: giảm chi phí đó thế nào? PostgreSQL cho hai công tắc liên quan trực tiếp — full_page_writes (nguồn gốc của FPI) và wal_compression (nén FPI). Bài này đo thật cả hai, và làm rõ vì sao một cái nên bật còn cái kia gần như không bao giờ nên tắt.
Hai công tắc, hai hướng ngược nhau
full_page_writes (mặc định on) chính là cơ chế tạo ra FPI. Khi bật, lần đầu sửa một trang sau checkpoint, PostgreSQL chép nguyên trang 8KB vào WAL để chống torn page — trường hợp máy sập lúc HĐH mới ghi được một nửa trang 8KB. Tắt nó đi thì wal_fpi = 0 và WAL nhỏ hẳn, nhưng mất luôn lá chắn chống torn page.
wal_compression (mặc định off) đi hướng ngược lại và an toàn hơn nhiều: nó giữ FPI (vẫn an toàn) nhưng nén chúng lại trước khi ghi vào WAL. Bạn giảm được kích thước WAL mà không hy sinh độ bền.
SHOW full_page_writes; -- on
SHOW wal_compression; -- off (giá trị: pglz | lz4 | zstd | on | off)
ALTER SYSTEM SET wal_compression = 'zstd'; -- hoặc lz4 (nhanh hơn)
SELECT pg_reload_conf();

Hình 1: full_page_writes (on) tạo ra FPI để chống torn page; tắt nó bỏ FPI nhưng mất an toàn. wal_compression (off) nén FPI lại — giảm WAL mà vẫn giữ an toàn. Chọn lz4 (nhanh) hoặc zstd (nén mạnh).
Đo thật: nén giảm 17-30% tuỳ độ nén của dữ liệu
Tôi dựng lại một bảng mới hoàn toàn cho mỗi phép đo (để số FPI cố định, chênh lệch chỉ đến từ nén), rồi UPDATE toàn bảng 200k dòng ngay sau CHECKPOINT, đọc pg_stat_wal:

Hình 2: Trên dữ liệu dễ nén (text lặp lại), wal_compression giảm WAL từ 91 MB xuống 63 MB (zstd, ~30%). Trên dữ liệu khó nén (md5), chỉ giảm còn 62 MB (~17%). full_page_writes=off bỏ hẳn FPI: 91→59 MB (~35%) nhưng mất an toàn.
Hai bộ số cho một bài học quan trọng: mức nén phụ thuộc hoàn toàn vào độ nén của dữ liệu.
- Dữ liệu dễ nén (text lặp lại như tên, trạng thái, ghi chú mặc định):
off91 MB →lz465 MB →zstd63 MB — giảm ~30%. - Dữ liệu khó nén (cột chứa md5, gần như ngẫu nhiên):
off75 MB →lz470 MB →zstd62 MB — chỉ ~17%.
FPI chứa nội dung nguyên trang, nên nếu bảng của bạn toàn text/số lặp lại thì nén rất hiệu quả; nếu toàn dữ liệu ngẫu nhiên (hash, UUID, ảnh, dữ liệu đã nén) thì lợi ích khiêm tốn hơn. Dù vậy, zstd gần như luôn thắng cả hai trường hợp.
full_page_writes=off: giảm nhiều nhất, nhưng đừng
Để so sánh, tôi tắt full_page_writes và đo cùng UPDATE: wal_fpi tụt về 0, WAL từ 91 MB xuống 59 MB — giảm ~35%, nhiều hơn cả nén. Hấp dẫn? Đừng.
Lý do FPI tồn tại: nếu máy sập giữa lúc HĐH đang ghi một trang 8KB, trang có thể "rách" — nửa nội dung cũ, nửa mới. Không có FPI trong WAL để dựng lại nguyên trang, PostgreSQL không thể sửa trang rách đó → hỏng dữ liệu âm thầm, đúng loại lỗi tệ nhất. full_page_writes=off chỉ an toàn khi tầng lưu trữ đảm bảo ghi 8KB nguyên tử (một số SAN/filesystem đặc thù rất hiếm). Với 99% hệ thống, để on. Tôi đã khôi phục full_page_writes=on ngay sau khi đo.
Kết luận đẹp: full_page_writes tạo ra chi phí FPI (cần cho an toàn), còn wal_compression là cách lấy lại phần lớn chi phí đó mà không mất an toàn. Đó là lý do đường đi đúng là giữ full_page_writes=on và bật wal_compression.
Đánh đổi cần cân nhắc
wal_compression đổi I/O lấy CPU. Nén tốn CPU khi ghi và giải nén khi phục hồi/replay. Trên hệ thống I/O là nút cổ chai (ghi nặng, đĩa chậm, băng thông replication hẹp), đánh đổi này rất đáng. Trên hệ thống CPU đã căng mà I/O dư dả, cân nhắc kỹ hơn — nhưng lz4 có chi phí CPU thấp đến mức thường vẫn nên bật.
lz4 hay zstd? lz4 nhanh, CPU thấp, nén vừa — mặc định tốt cho OLTP nhạy độ trễ. zstd nén mạnh hơn (như đo thấy), CPU cao hơn chút — tốt khi mục tiêu là giảm tối đa WAL (băng thông replication, chi phí lưu trữ WAL archive). pglz là bản cũ, có sẵn mọi build nhưng yếu hơn lz4 gần như mọi mặt.
Lợi ích thật vượt xa kích thước WAL trên đĩa. WAL nhỏ hơn nghĩa là: ít băng thông replication, backup/archive nhỏ hơn, checkpoint recycle WAL dễ hơn, và phục hồi replay ít dữ liệu hơn. Với hệ thống có replica hoặc PITR, giảm 20-30% WAL là lợi ích lan tỏa đáng kể.
Ba ý mang về
- full_page_writes tạo ra FPI để chống torn page, wal_compression nén FPI để giảm WAL an toàn — hai công tắc đi ngược hướng: một cái là nguồn chi phí (cần cho an toàn), một cái là cách thu hồi chi phí mà vẫn giữ an toàn.
- Bật wal_compression giảm WAL 17-30% tuỳ độ nén dữ liệu: đo thật, dữ liệu text lặp lại giảm 30% (91→63 MB zstd), dữ liệu md5 gần ngẫu nhiên chỉ 17% (75→62 MB) — FPI vẫn còn nên vẫn an toàn; chọn lz4 (nhanh) hoặc zstd (nén mạnh).
- Gần như không bao giờ tắt full_page_writes: đo thật nó giảm 35% (91→59 MB, wal_fpi=0) nhưng bỏ hẳn chống torn page → máy sập có thể hỏng dữ liệu âm thầm, chỉ an toàn khi lưu trữ ghi 8KB nguyên tử (rất hiếm) — giữ on, bật wal_compression thay thế.
Phần sau ta xét một tối ưu ở tầng bộ nhớ hệ điều hành: Phần sau mổ xẻ huge pages — vì sao trang nhớ 2MB thay vì 4KB giảm áp lực lên TLB cho shared_buffers lớn, và cách bật.