Bài trước log_min_duration_statement bắt được câu chậm và ghi nguyên văn nó vào log. Nhưng nguyên văn thôi chưa đủ — bạn cần kế hoạch của đúng lần chạy chậm đó để biết vì sao. Vấn đề: khi bạn chạy lại EXPLAIN ANALYZE, dữ liệu đã khác, cache đã nóng, và bạn không tái hiện được cơn chậm. Giải pháp là auto_explain — nó tự đính kèm kế hoạch đầy đủ vào log ngay lúc câu chậm xảy ra.

Tự chụp kế hoạch, ngay khi chậm

auto_explain là một contrib module: khi một truy vấn vượt ngưỡng thời gian, nó tự ghi EXPLAIN (thậm chí EXPLAIN ANALYZE) của câu đó vào log. Bạn không phải có mặt, không phải chạy lại gì cả.

Ảnh chụp mã SQL nền tối bật và cấu hình auto_explain. Nạp module cần trong shared_preload_libraries đã có pg_stat_statements. ALTER SYSTEM SET shared_preload_libraries bằng pg_stat_statements phẩy auto_explain rồi khởi động lại PostgreSQL. Cấu hình toàn hệ thống hoặc một phiên. SET auto_explain.log_min_duration bằng 50 chỉ ghi câu lớn hơn 50 ms. SET auto_explain.log_analyze bằng on chạy như EXPLAIN ANALYZE số thực. SET auto_explain.log_buffers bằng on kèm shared hit read. SET auto_explain.log_nested_statements bằng on cả câu trong hàm hoặc PL. Rồi chạy truy vấn chậm bình thường SELECT count sao FROM pgbench_accounts WHERE abalance lớn hơn 500. PostgreSQL tự ghi kế hoạch đầy đủ vào log không cần chạy lại EXPLAIN. Cảnh báo log_analyze bằng on thêm chi phí đo cho mọi câu nên chỉ bật khi cần hoặc đặt log_min_duration cao để giới hạn phạm vi

Hình 1: Bật auto_explain (thêm vào preload, khởi động lại), rồi cấu hình. log_min_duration là ngưỡng (chỉ chụp câu vượt). log_analyze = on biến nó thành EXPLAIN ANALYZE — cho số thực, không chỉ ước lượng. log_buffers = on thêm số trang cache/đĩa. log_nested_statements = on bắt cả câu chạy bên trong hàm/thủ tục.

Log thật: cả kế hoạch ANALYZE, không cần chạy lại

Đặt ngưỡng 50 ms với log_analyze, chạy một câu chậm, rồi đọc log:

Ảnh chụp log thật nền tối kế hoạch ANALYZE của truy vấn chậm tự động. Lệnh docker logs pg-lab. Dòng LOG duration 78.046 ms màu đỏ plan. Query Text SELECT count sao FROM pgbench_accounts WHERE abalance lớn hơn 500 màu xanh dương. Aggregate cost 110280.03 tới 110280.04 rows 1 actual time 76.437 tới 78.034 rows 1 màu vàng. Buffers shared read 82932. Mũi tên Gather Workers Launched 2 actual time 1.896 tới 76.764 rows 45280. Mũi tên Parallel Seq Scan on pgbench_accounts màu xanh actual time 1.508 tới 69.054. Filter abalance lớn hơn 500. Rows Removed by Filter 1651573 màu đỏ. Buffers shared read 82932. Chú thích đây là EXPLAIN ANALYZE BUFFERS đầy đủ của lần chạy chậm thật thời gian thực số dòng thực buffer thực mà không cần chạy lại truy vấn để tái hiện

Hình 2: Thật. Log ghi duration: 78.046 ms plan: rồi toàn bộ cây kế hoạch với actual time, rows, và Buffers — đúng như EXPLAIN (ANALYZE, BUFFERS) mà ta học ở các bài đầu. Nhìn là thấy ngay thủ phạm: Parallel Seq Scan quét cả bảng, vứt bỏ 1.651.573 dòng không khớp, đọc 82.932 trang từ đĩa. Tất cả được ghi lại tự động, lúc câu chậm xảy ra — không cần bạn có mặt hay chạy lại.

Vì sao đây là mảnh ghép quan trọng

Ba công cụ giám sát ta đã học ghép thành một quy trình hoàn chỉnh:

  • pg_stat_statements (bài 7): truy vấn nào tốn tổng nhiều nhất — nhìn bức tranh lớn.
  • log_min_duration_statement (bài 8): lần chạy nào chậm, kèm tham số thật.
  • auto_explain (bài này): vì sao lần đó chậm — kế hoạch thực tế của chính nó.

Đặc biệt với những cơn chậm không tái hiện được (dữ liệu lệch theo thời điểm, cache lạnh, một tham số hiếm), auto_explain là cách duy nhất bắt được kế hoạch thật. Không có nó, bạn chỉ biết "câu này thỉnh thoảng chậm" mà không bao giờ thấy được kế hoạch lúc nó chậm.

Cái giá phải cân nhắc

log_analyze = on không miễn phí — nó bật đo lường (instrumentation) ở mức như EXPLAIN ANALYZE, và chi phí này áp lên mọi truy vấn chạy qua (dù chỉ câu vượt ngưỡng mới được ghi). Trên hệ thống tải rất cao, chi phí đo có thể đáng kể. Cách dùng an toàn:

  • Đặt log_min_duration đủ cao (vài trăm ms) để chỉ quan tâm câu thật chậm.
  • Bật log_analyze có chọn lọc — chỉ khi đang truy một vấn đề, hoặc chỉ trên một user/session, rồi tắt.
  • auto_explain.sample_rate (< 1.0) để chỉ chụp một phần các câu, giảm tải khi cần theo dõi liên tục.
  • Nếu chỉ cần kế hoạch ước lượng (không đo thật), để log_analyze = off — nhẹ hơn nhiều, vẫn thấy cấu trúc kế hoạch.

Ba ý mang về

  1. auto_explain tự ghi kế hoạch của truy vấn chậm vào log — đã thấy thật: một câu 78 ms được log kèm EXPLAIN (ANALYZE, BUFFERS) đầy đủ (actual time, rows, Buffers, Rows Removed by Filter), không cần chạy lại.
  2. Nó là mảnh ghép cuối của bộ ba giám sát: pg_stat_statements (tổng thể) + log_min_duration_statement (lần chậm) + auto_explain (vì sao) — đặc biệt vô giá với cơn chậm không tái hiện được.
  3. log_analyze=on có chi phí đo trên mọi câu — đặt log_min_duration cao, bật chọn lọc, dùng sample_rate để kiểm soát; hoặc để log_analyze=off nếu chỉ cần kế hoạch ước lượng.

Ta đã trọn bộ công cụ đo lường: EXPLAIN, BUFFERS, cost, và ba tầng giám sát. Còn một cách nữa để đọc kế hoạch dễ hơn với truy vấn phức tạp. Phần sau dùng EXPLAIN (FORMAT JSON) và các công cụ trực quan để biến cây kế hoạch chằng chịt thành sơ đồ dễ nhìn — khép lại mô-đun nền tảng đo lường trước khi ta bước vào thế giới index.