The auto_explain PostgreSQL module provides a means for logging execution plans of slow statements automatically, without having to run EXPLAIN by hand. This is especially helpful for tracking down un-optimized queries in large applications.

Enable auto_explain

To enable the auto_explain extension, add auto_explain to shared_preload_libraries in the PostgreSQL server configuration parameters using the YB-TServer --ysql_pg_conf_csv flag:

--ysql_pg_conf_csv=shared_preload_libraries=auto_explain

Note that modifying shared_preload_libraries requires restarting the YB-TServer.

Customize auto_explain

You can customize the following auto_explain parameters:

Parameter Description Default
log_min_duration Minimum statement execution time, in milliseconds, that will cause the statement's plan to be logged. Setting this to zero logs all plans. Minus-one (the default) disables logging. For example, if you set it to 250ms then all statements that run 250ms or longer will be logged. Only superusers can change this setting. -1
log_analyze Print EXPLAIN ANALYZE output, rather than just EXPLAIN output when an execution plan is logged. When on, per-plan-node timing occurs for all statements executed, whether or not they run long enough to actually get logged. This can have an extremely negative impact on performance. Turning off log_timing ameliorates the performance cost, at the price of obtaining less information. Only superusers can change this setting. false
log_buffers Print buffer usage statistics when an execution plan is logged; equivalent to the BUFFERS option of EXPLAIN. Has no effect unless log_analyze is enabled. Only superusers can change this setting. false
log_timing Print per-node timing information when an execution plan is logged; equivalent to the TIMING option of EXPLAIN. The overhead of repeatedly reading the system clock can slow down queries significantly on some systems, so it may be beneficial to set this parameter to off when only actual row counts, and not exact times, are needed. Has no effect unless log_analyze is enabled. Only superusers can change this setting. true
log_triggers Include trigger execution statistics when an execution plan is logged. Has no effect unless log_analyze is enabled. Only superusers can change this setting. false
log_verbose Print verbose details when an execution plan is logged; equivalent to the VERBOSE option of EXPLAIN. Only superusers can change this setting. false
log_format The format of the EXPLAIN output. Allowed values are text, xml, json, and yaml. Only superusers can change this setting. text
log_nested_statements Consider nested statements (statements executed inside a function) for logging. When off, only top-level query plans are logged. Only superusers can change this setting. false
sample_rate Explain only a set fraction of the statements in each session. The default 1 means explain all the queries. In case of nested statements, either all will be explained or none. Only superusers can change this setting. 1
log_dist Set to false to disable the DIST option of EXPLAIN ANALYZE. True by default, equivalent to EXPLAIN (ANALYZE, DIST). This setting only applies when log_analyze is true. true

Note that the default behavior is to do nothing, so you must set at least auto_explain.log_min_duration if you want any results.

Example

To change auto_explain parameters, use the SET statement. For example:

SET auto_explain.log_min_duration = 0;
SET auto_explain.log_analyze = true;
SELECT count(*)
    FROM pg_class, pg_index
    WHERE oid = indrelid AND indisunique;

This produces log output similar to the following in the PostgreSQL log file in the tserver/logs directory:

LOG:  duration: 316.556 ms  plan:
        Query Text: SELECT count(*)
                   FROM pg_class, pg_index
                   WHERE oid = indrelid AND indisunique;
        Aggregate  (cost=216.39..216.40 rows=1 width=8) (actual time=316.489..316.489 rows=1 loops=1)
          ->  Nested Loop  (cost=0.00..213.89 rows=1000 width=0) (actual time=10.828..316.200 rows=110 loops=1)
                ->  Seq Scan on pg_index  (cost=0.00..100.00 rows=1000 width=4) (actual time=7.465..8.068 rows=110 loops=1)
                      Storage Filter: indisunique
                ->  Index Scan using pg_class_oid_index on pg_class  (cost=0.00..0.11 rows=1 width=4) (actual time=2.673..2.673 rows=1 loops=110)
                      Index Cond: (oid = pg_index.indrelid)