pg_stat_statements (bài trước) cho biết truy vấn nào tốn thời gian ngay bây giờ, nhưng nó sống trong RAM và mất sạch khi reset hay restart. Khi bạn cần phân tích quá khứ — chuyện gì đã xảy ra lúc 2 giờ sáng khi hệ thống chậm, giờ nào là cao điểm, lỗi gì đã bùng phát — công cụ đúng là pgBadger: nó đọc log của PostgreSQL và sinh một báo cáo HTML trực quan với biểu đồ và bảng xếp hạng. Bài này dựng thật từ đầu: cấu hình log, sinh traffic, rồi chạy pgBadger trên log thật để xem báo cáo.
Bước 1: cấu hình log cho pgBadger đọc được
pgBadger phân tích cú pháp log, nên log phải có định dạng nó hiểu. Ba tham số quan trọng:
-- postgresql.conf (qua ALTER SYSTEM):
logging_collector = on
log_line_prefix = '%t [%p]: user=%u,db=%d,app=%a,client=%h '
log_min_duration_statement = 0 -- 0 = log MỌI câu (để demo)
-- thật: 200-1000 (chỉ log câu chậm)
log_checkpoints = on
log_connections = on
log_disconnections = on
log_line_prefix phải chứa thời gian (%t), pid (%p) và các trường pgBadger cần để gom nhóm. log_min_duration_statement quyết định câu nào được log: đặt 0 log tất cả (tốn đĩa, chỉ để phân tích ngắn), còn trên production thường đặt 200–1000 ms để chỉ bắt câu chậm.

Hình 1: Cấu hình log_line_prefix và log_min_duration_statement cho pgBadger, rồi chạy pgbadger -f stderr <log> -o report.html. pgBadger khác pg_stat_statements ở chỗ phân tích log lịch sử, offline.
Bước 2: chạy pgBadger trên log thật
Sau khi bật log, tôi sinh traffic đa dạng: một truy vấn quét bảng chạy vài lần, hàng trăm nghìn point-lookup qua pgbench, và cố ý gây vài lỗi (chèn trùng khoá, truy vấn bảng không tồn tại). Log phình lên 31 MB. Rồi chạy:
pgbadger -f stderr /var/lib/postgresql/data/log/pg-20260922.log -o report.html
pgBadger 13.2 phân tích toàn bộ và sinh một báo cáo HTML 974 KB với đầy đủ biểu đồ và bảng. Đây là số liệu thật trích từ báo cáo:

Hình 2: Báo cáo pgBadger thật: 192.222 query gom thành 9 dạng chuẩn hoá, tổng 5s210ms, peak 45.483 query/giây, 3 lỗi. Top tốn tổng thời gian: point-lookup 4s751ms qua 192.211 lần — pgBadger tự rút ra từ log.
Con số thật từ báo cáo:
- 192.222 query gom thành chỉ 9 dạng chuẩn hoá — pgBadger tự chuẩn hoá tham số như
pg_stat_statements. - Tổng thời gian thực thi 5s210ms, peak 45.483 query/giây lúc 17:41:40.
- 3 lỗi (3 dạng chuẩn hoá) — chèn trùng khoá, bảng không tồn tại.
- Top tốn tổng thời gian: point-lookup
WHERE aid = $1— 4s751ms qua 192.211 lần, áp đảo; full scancount(*)— 387ms qua 4 lần.
Kết quả này lặp lại đúng bài học của bài trước: cái point-lookup nhỏ xíu, chạy 192.211 lần, mới là nơi tốn tổng thời gian nhiều nhất — và pgBadger tự động rút ra điều đó từ log, không cần bạn viết truy vấn. Ngoài ra báo cáo còn có biểu đồ traffic theo giờ, phân bố thời gian truy vấn, danh sách lỗi — những thứ pg_stat_statements không cho.
pgBadger khác pg_stat_statements ở đâu
Hai công cụ bổ sung cho nhau:
pg_stat_statements: trực tuyến, trong RAM, cực nhanh để xem hiện tại. Nhưng mất khi reset/restart, không lưu diễn biến theo thời gian, không thấy lỗi hay checkpoint.- pgBadger: phân tích log lịch sử, offline (không tải server lúc chạy — bạn copy log ra máy khác mà phân tích). Thấy được giờ cao điểm, diễn biến theo thời gian, lỗi, checkpoint, tạm khoá — bức tranh rộng hơn nhiều, nhưng chỉ có những gì đã được log.
Nói ngắn: dùng pg_stat_statements để chẩn đoán nhanh hiện tại, dùng pgBadger để phân tích sâu quá khứ và làm báo cáo định kỳ.
Đánh đổi cần cân nhắc
Log tốn đĩa và có chi phí ghi. log_min_duration_statement = 0 log mọi câu — 31 MB chỉ trong ~20 giây traffic của tôi. Trên production tải cao, log tất cả sẽ đầy đĩa nhanh và bản thân việc ghi log cũng tốn I/O. Đặt ngưỡng hợp lý (200–1000 ms) để chỉ bắt câu chậm đáng quan tâm, và luân chuyển (rotate) log.
pgBadger phân tích được là nhờ log đúng định dạng. Nếu log_line_prefix thiếu trường pgBadger cần, hoặc log ở định dạng lạ, báo cáo sẽ rỗng hoặc sai. Dùng đúng mẫu prefix pgBadger khuyến nghị. Với log lớn (nhiều GB), dùng -j để chạy song song và -I (incremental) để phân tích tăng dần hằng ngày thay vì quét lại từ đầu.
Chỉ thấy những gì đã log. Nếu log_min_duration_statement đặt 1000 ms, những câu 900 ms chạy triệu lần sẽ không vào log và pgBadger không thấy chúng — khác pg_stat_statements vốn bắt mọi câu. Đây là đánh đổi giữa chi phí log và độ phủ; cân theo nhu cầu.
Ba ý mang về
- pgBadger biến log PostgreSQL thành báo cáo hiệu năng trực quan: dựng thật, phân tích log 31 MB sinh báo cáo HTML thấy 192.222 query gom thành 9 dạng, peak 45.483 query/giây, 3 lỗi — cùng biểu đồ giờ cao điểm và top truy vấn mà không cần viết truy vấn nào.
- Cấu hình log đúng là điều kiện tiên quyết:
log_line_prefixchuẩn +log_min_duration_statement(0 để demo, 200–1000 ms cho production để chỉ bắt câu chậm) +logging_collector— pgBadger chỉ mạnh khi log có đủ thông tin nó cần. - pgBadger bổ sung cho
pg_stat_statements, không thay thế: một cái phân tích log lịch sử offline (thấy diễn biến, giờ cao điểm, lỗi), cái kia xem trực tuyến trong RAM (nhanh, bắt mọi câu nhưng mất khi restart) — dùng cả hai cho bức tranh đầy đủ.
Phần sau ta chuyển từ quan sát sang chủ động: Phần sau bàn về việc cần cảnh báo những gì trên một PostgreSQL production — các chỉ số quan trọng nhất để đặt ngưỡng báo động trước khi sự cố thành thảm hoạ.