Bài trước pg_stat_statements cho ta bức tranh tổng thể: truy vấn nào tốn nhiều thời gian tích luỹ. Nhưng nó có điểm mù: nó chỉ lưu trung bình, và gộp mọi tham số thành một khuôn. Khi một truy vấn thỉnh thoảng chậm đột biến — với một tham số cụ thể, vào một thời điểm cụ thể — bạn cần bắt tận tay câu đó, kèm nguyên văn giá trị. Công cụ cho việc này là log_min_duration_statement.
Một ngưỡng, tự động ghi mọi câu vượt
Ý tưởng rất đơn giản: đặt một ngưỡng thời gian (mili-giây), và PostgreSQL sẽ tự ghi vào log mọi câu lệnh chạy lâu hơn thế — kèm thời gian thật và nguyên văn câu lệnh với tham số cụ thể.

Hình 1: Ba cách đặt ngưỡng, tuỳ phạm vi. Toàn hệ thống (ALTER SYSTEM + pg_reload_conf() — không cần khởi động lại). Một phiên (SET, tiện khi đang gỡ lỗi). Một user/database (ALTER ROLE ... SET — khoanh vùng để không làm log ồn cả hệ thống). Giá trị -1 tắt hẳn, 0 ghi mọi câu (chỉ dùng khi debug ngắn — rất ồn).
Log thật: câu chậm bị ghi, câu nhanh thì không
Đặt ngưỡng 50 ms, chạy một câu chậm và một câu nhanh, rồi đọc log của PostgreSQL:

Hình 2: Thật. Câu SELECT count(*) ... WHERE abalance > 500 chạy 98,203 ms — vượt ngưỡng 50 ms nên vào log với đúng chữ duration: 98.203 ms statement: ... kèm nguyên văn câu lệnh. Câu tra aid = 12345 chạy dưới 1 ms nên không có dòng log nào. Bạn không phải ngồi canh — PostgreSQL tự tố cáo mọi câu vượt ngưỡng.
Vì sao nó bổ sung cho pg_stat_statements
Hai công cụ trả lời hai câu hỏi khác nhau, dùng chung mới đủ:
pg_stat_statements |
log_min_duration_statement |
|
|---|---|---|
| Cho biết | Truy vấn nào tốn tổng nhiều nhất | Lần chạy cụ thể nào chậm |
| Lưu gì | Số liệu gộp theo khuôn | Nguyên văn câu + tham số thật |
| Thấy đột biến? | Khó (chỉ trung bình + stddev) | Có — bắt đúng lần chậm |
| Có tham số? | Không (gộp) | Có (giá trị cụ thể) |
Ví dụ điển hình: một truy vấn trung bình 5 ms nhưng với một customer_id có cực nhiều dữ liệu thì mất 3 giây. pg_stat_statements chỉ thấy trung bình 5 ms (không báo động); log_min_duration_statement bắt được đúng lần 3 giây kèm customer_id gây họa.
Đặt ngưỡng bao nhiêu, và cái bẫy
- Production: thường 200–1000 ms — đủ cao để chỉ bắt câu thật sự chậm, không làm log ngập.
- Gỡ lỗi: hạ tạm xuống 50 ms hoặc thấp hơn trong một phiên/một user, rồi trả lại.
- Cạm bẫy
log_min_duration_statement = 0: ghi mọi câu, kể cả hàng trăm nghìn câu nhanh — làm phình đĩa log và tự nó làm chậm hệ thống (ghi log cũng tốn I/O). Chỉ dùng trong vài phút khi thật cần. log_statement = 'all'khác hẳn: nó ghi mọi câu bất kể thời gian và không kèm thời gian chạy — không phải thứ bạn muốn để tìm câu chậm.
Đọc log ở đâu và cùng ai
Dòng log đi tới đích cấu hình bởi log_destination (stderr, csvlog, hoặc syslog). Trên nhiều hệ thống, bật logging_collector = on để PostgreSQL tự xoay file log trong thư mục log/. Vài tuỳ chọn đi kèm đáng bật:
log_line_prefix— thêm thời gian, PID, user, database, application vào đầu mỗi dòng để lần ra ai chạy câu chậm.log_lock_waits = on— ghi khi một câu chờ khóa quá lâu (một nguồn chậm không thấy trong thời gian thực thi thuần).- Công cụ pgBadger (bài sau) đọc những dòng log này và dựng báo cáo trực quan.
Ba ý mang về
log_min_duration_statementtự động ghi mọi câu vượt ngưỡng — đã thấy thật: câu 98 ms vào log kèm nguyên văn, câu <1 ms thì không. Đặt bằngALTER SYSTEM+pg_reload_conf(), hoặcSET/ALTER ROLEđể khoanh phạm vi.- Nó bổ sung cho
pg_stat_statements: cái kia cho tổng thể và trung bình, cái này bắt lần chậm cá biệt kèm tham số thật — thứ cần để tái hiện và gỡ. - Chọn ngưỡng vừa phải (200–1000 ms cho production); tránh
= 0vì ghi mọi câu làm phình log và tự làm chậm. Bật thêmlog_line_prefixvàlog_lock_waitsđể có ngữ cảnh.
Log cho bạn câu nào chậm và bao lâu. Nhưng để biết vì sao chậm, bạn cần kế hoạch của chính lần chạy đó. Phần sau dùng auto_explain để tự động đính kèm EXPLAIN (thậm chí ANALYZE) vào log cho những truy vấn chậm — bắt được cả kế hoạch thật của ca cá biệt mà không cần chạy lại.