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.

Ảnh chụp đoạn mã SQL nền tối minh hoạ pgBadger biến log PostgreSQL thành báo cáo hiệu năng trực quan PostgreSQL 16 phân tích log offline không tốn tài nguyên server lúc chạy, cấu hình log để pgBadger đọc được postgresql.conf logging_collector on log_line_prefix thời gian pid user db app client log_min_duration_statement 0 log mọi câu demo thật 200-1000 chỉ câu chậm log_checkpoints on log_connections on log_disconnections on restart để bật logging_collector, chạy pgBadger trên file log pgbadger -f stderr path log pg-20260922.log -o report.html -f định dạng log stderr csvlog jsonlog -o file báo cáo HTML có biểu đồ bảng xếp hạng -j N chạy song song N luồng cho log lớn -I chế độ tăng dần, pgBadger tổng hợp gì từ log truy vấn tốn tổng thời gian nhất chuẩn hoá gom theo dạng truy vấn chậm nhất mỗi lần truy vấn thường xuyên nhất biểu đồ traffic theo giờ giờ cao điểm queries mỗi giây lỗi checkpoint kết nối tạm khoá tạm file, khác pg_stat_statements ở đâu pg_stat_statements trực tuyến trong RAM mất khi reset restart pgBadger từ log lịch sử phân tích quá khứ giữ theo thời gian thấy được giờ cao điểm và diễn biến chạy offline không tải server

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:

Ảnh chụp bảng kết quả đo thật nền tối báo cáo pgBadger 13.2 từ log pg-lab PostgreSQL 16 đã phân tích file log 31 MB sinh báo cáo HTML 974 KB, tổng quan global stats số liệu thật từ báo cáo number of queries 192222 number of unique normalized queries 9 total query duration execute 5s210ms query peak 45483 queries mỗi giây lúc 17:41:40 number of events lỗi 3 3 dạng chuẩn hoá khoảng thời gian log 17:41:26 tới 17:41:44, top truy vấn tốn tổng thời gian pgBadger tự chuẩn hoá cộng gom dạng truy vấn SELECT abalance WHERE aid bằng dollar1 tổng thời gian 4s751ms số lần 192211 SELECT count WHERE abalance lớn hơn dollar1 387ms 4 lần INSERT generate_series 68ms 1 lần, giống bài trước point-lookup nhỏ nhưng chạy 192211 lần tốn tổng nhiều nhất pgBadger rút ra điều này tự động từ log kèm biểu đồ giờ cao điểm và lỗi, vài dòng log slow query thật nguồn để pgBadger phân tích 17:41:39 user lab db lab LOG duration 113.461 ms statement SELECT count từ pgbench_accounts WHERE abalance lớn hơn 500 ERROR duplicate key value violates unique constraint

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 scan count(*) — 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ề

  1. 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.
  2. Cấu hình log đúng là điều kiện tiên quyết: log_line_prefix chuẩ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.
  3. 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ạ.