支援的版本: 目前 (17) / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

EXPLAIN

EXPLAIN — 顯示陳述式的執行計畫

概要

EXPLAIN [ ( option [, ...] ) ] statement

where option can be one of:

    ANALYZE [ boolean ]
    VERBOSE [ boolean ]
    COSTS [ boolean ]
    SETTINGS [ boolean ]
    GENERIC_PLAN [ boolean ]
    BUFFERS [ boolean ]
    SERIALIZE [ { NONE | TEXT | BINARY } ]
    WAL [ boolean ]
    TIMING [ boolean ]
    SUMMARY [ boolean ]
    MEMORY [ boolean ]
    FORMAT { TEXT | XML | JSON | YAML }

描述

這個指令會顯示 PostgreSQL 規劃器為提供的陳述式產生的執行計畫。執行計畫會顯示陳述式引用的資料表將如何掃描 — 透過簡單的循序掃描、索引掃描等等 — 以及如果引用了多個資料表,將使用哪些聯結演算法來將每個輸入資料表中的必要列組合在一起。

顯示中最關鍵的部分是預估的陳述式執行成本,這是規劃器對執行陳述式所需時間的猜測(以任意的成本單位衡量,但通常表示磁碟頁面提取)。實際上顯示了兩個數字:傳回第一列之前的啟動成本,以及傳回所有列的總成本。對於大多數查詢,總成本才是重要的,但在諸如 EXISTS 中的子查詢等情況下,規劃器會選擇最小的啟動成本而不是最小的總成本(因為執行器在取得一列後會停止)。此外,如果您使用 LIMIT 子句限制要傳回的列數,規劃器會在端點成本之間進行適當的插值,以估計哪個計畫才是真正最便宜的。

ANALYZE 選項會導致實際執行陳述式,而不僅僅是規劃。然後,實際的執行時間統計資料會新增到顯示中,包括每個計畫節點中花費的總經過時間(以毫秒為單位)以及它實際傳回的總列數。這對於查看規劃器的估計是否接近實際情況很有用。

重要事項

請記住,使用 ANALYZE 選項時,實際上會執行該陳述式。雖然 EXPLAIN 會捨棄 SELECT 將傳回的任何輸出,但陳述式的其他副作用會像往常一樣發生。如果您希望在 INSERTUPDATEDELETEMERGECREATE TABLE ASEXECUTE 陳述式上使用 EXPLAIN ANALYZE,而不讓該指令影響您的資料,請使用此方法

BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;

參數

ANALYZE

執行該指令並顯示實際的執行時間和其他統計資料。此參數預設為 FALSE

VERBOSE

顯示有關計畫的額外資訊。具體來說,包括計畫樹中每個節點的輸出欄清單,以 schema 限定資料表和函式名稱,始終使用範圍資料表別名標記運算式中的變數,並始終列印顯示統計資料的每個觸發程序的名稱。如果已計算查詢識別碼,也會顯示該識別碼,請參閱 compute_query_id 以取得更多詳細資訊。此參數預設為 FALSE

COSTS

包括有關每個計畫節點的預估啟動成本和總成本,以及預估的列數和每列的預估寬度的資訊。此參數預設為 TRUE

SETTINGS

包括有關組態參數的資訊。具體來說,包括影響查詢規劃且值與內建預設值不同的選項。此參數預設為 FALSE

GENERIC_PLAN

允許陳述式包含參數預留位置,例如 $1,並產生不依賴於這些參數值的泛型計畫。有關泛型計畫和支援參數的陳述式類型的詳細資訊,請參閱 PREPARE。此參數不能與 ANALYZE 一起使用。它預設為 FALSE

BUFFERS

包含 buffer 使用量的資訊。具體來說,包含下列數字:共享區塊的命中、讀取、標記為髒區塊和寫入次數,本機區塊的命中、讀取、標記為髒區塊和寫入次數,臨時區塊的讀取和寫入次數,以及讀取和寫入資料檔案區塊、本機區塊和臨時檔案區塊所花費的時間(以毫秒為單位),如果 track_io_timing 已啟用。命中 表示當需要該區塊時,因為該區塊已經在快取中找到,所以避免了讀取。共享區塊包含來自常規資料表和索引的資料;本機區塊包含來自臨時資料表和索引的資料;而臨時區塊包含用於排序、雜湊、具體化計畫節點和類似情況的短期工作資料。標記為髒區塊的區塊數量表示此查詢變更了先前未修改的區塊數量;而寫入的區塊數量表示在查詢處理期間,此後端從快取中逐出的先前已標記為髒區塊的數量。較高層級節點顯示的區塊數量包括其所有子節點使用的區塊數量。在文字格式中,僅會列印非零值。此參數預設為 FALSE

SERIALIZE

包含關於序列化查詢輸出資料成本的資訊,也就是將其轉換為文字或二進位格式以傳送給用戶端。如果資料類型輸出函式很昂貴,或者TOASTed 值必須從行外儲存空間取得。 EXPLAIN 的預設行為 SERIALIZE NONE 不會執行這些轉換。如果指定 SERIALIZE TEXTSERIALIZE BINARY,則會執行適當的轉換,並測量執行此操作所花費的時間(除非指定 TIMING OFF)。如果也指定了 BUFFERS 選項,則也會計算轉換中涉及的任何 buffer 存取。但是,在任何情況下,EXPLAIN 實際上都不會將產生的資料傳送到用戶端;因此無法透過這種方式來調查網路傳輸成本。只有在啟用 ANALYZE 時,才能啟用序列化。如果 SERIALIZE 在沒有引數的情況下寫入,則假定為 TEXT

WAL

包含關於 WAL 記錄產生的資訊。具體來說,包含記錄的數量、完整頁面影像 (fpi) 的數量以及以位元組為單位產生的 WAL 大小。在文字格式中,僅會列印非零值。只有在啟用 ANALYZE 時,才能使用此參數。它預設為 FALSE

TIMING

包含輸出中每個節點的實際啟動時間和花費的時間。重複讀取系統時鐘的額外負荷可能會在某些系統上顯著降低查詢速度,因此,如果只需要實際的列計數,而不需要精確的時間,則將此參數設定為 FALSE 可能很有用。即使使用此選項關閉了節點層級的時間測量,也始終會測量整個陳述式的執行時間。只有在啟用 ANALYZE 時,才能使用此參數。它預設為 TRUE

SUMMARY

在查詢計畫之後包含摘要資訊(例如,合計的時間資訊)。預設情況下,當使用 ANALYZE 時,會包含摘要資訊,否則預設情況下不會包含,但可以使用此選項啟用。EXPLAIN EXECUTE 中的規劃時間包括從快取中取得計畫所需的時間以及重新規劃所需的時間(如果需要)。

MEMORY

包含關於查詢規劃階段記憶體消耗的資訊。具體來說,包含規劃器記憶體結構使用的精確儲存空間量,以及考慮到配置額外負荷的總記憶體。此參數預設為 FALSE

FORMAT

指定輸出格式,可以是 TEXT、XML、JSON 或 YAML。非文字輸出包含與文字輸出格式相同的資訊,但更容易讓程式剖析。此參數預設為 TEXT

boolean

指定是否應啟用或停用選定的選項。您可以寫入 TRUEON1 來啟用該選項,並寫入 FALSEOFF0 來停用它。也可以省略 boolean 值,在這種情況下,假定為 TRUE

statement

任何 SELECTINSERTUPDATEDELETEMERGEVALUESEXECUTEDECLARECREATE TABLE ASCREATE MATERIALIZED VIEW AS 陳述式,您希望看到其執行計畫。

Outputs

命令的結果是為 statement 選擇的計畫的文字描述,可以選擇使用執行統計資料進行註解。Section 14.1 描述了提供的資訊。

Notes

為了讓 PostgreSQL 查詢規劃器在最佳化查詢時做出合理且明智的決策,用於查詢的所有資料表的 pg_statistic 資料應該是最新的。通常,自動清理精靈會自動處理此事。但是,如果資料表最近的內容發生了重大變更,則可能需要手動執行 ANALYZE,而不是等待自動清理趕上變更。

為了測量執行計畫中每個節點的執行時間成本,目前 EXPLAIN ANALYZE 的實作會將分析額外負荷新增到查詢執行中。因此,在查詢上執行 EXPLAIN ANALYZE 有時可能比正常執行查詢花費的時間長得多。額外負荷的大小取決於查詢的性質以及使用的平台。最糟糕的情況是計畫節點本身每次執行所需的時間非常少,並且機器獲取當天時間的作業系統呼叫相對較慢。

Examples

顯示具有單一 integer 欄位和 10000 列的資料表上的簡單查詢的計畫

EXPLAIN SELECT * FROM foo;

                       QUERY PLAN
---------------------------------------------------------
 Seq Scan on foo  (cost=0.00..155.00 rows=10000 width=4)
(1 row)

這是相同的查詢,採用 JSON 輸出格式

EXPLAIN (FORMAT JSON) SELECT * FROM foo;
           QUERY PLAN
--------------------------------
 [                             +
   {                           +
     "Plan": {                 +
       "Node Type": "Seq Scan",+
       "Relation Name": "foo", +
       "Alias": "foo",         +
       "Startup Cost": 0.00,   +
       "Total Cost": 155.00,   +
       "Plan Rows": 10000,     +
       "Plan Width": 4         +
     }                         +
   }                           +
 ]
(1 row)

如果存在索引,並且我們使用具有可索引 WHERE 條件的查詢,則 EXPLAIN 可能會顯示不同的計畫

EXPLAIN SELECT * FROM foo WHERE i = 4;

                         QUERY PLAN
--------------------------------------------------------------
 Index Scan using fi on foo  (cost=0.00..5.98 rows=1 width=4)
   Index Cond: (i = 4)
(2 rows)

這是相同的查詢,但採用 YAML 格式

EXPLAIN (FORMAT YAML) SELECT * FROM foo WHERE i='4';
          QUERY PLAN
-------------------------------
 - Plan:                      +
     Node Type: "Index Scan"  +
     Scan Direction: "Forward"+
     Index Name: "fi"         +
     Relation Name: "foo"     +
     Alias: "foo"             +
     Startup Cost: 0.00       +
     Total Cost: 5.98         +
     Plan Rows: 1             +
     Plan Width: 4            +
     Index Cond: "(i = 4)"
(1 row)

XML 格式留給讀者作為練習。

這是相同的計畫,但隱藏了成本估算

EXPLAIN (COSTS FALSE) SELECT * FROM foo WHERE i = 4;

        QUERY PLAN
----------------------------
 Index Scan using fi on foo
   Index Cond: (i = 4)
(2 rows)

這是一個查詢的查詢計畫範例,該查詢使用聚合函式

EXPLAIN SELECT sum(i) FROM foo WHERE i < 10;

                             QUERY PLAN
-------------------------------------------------------------------​--
 Aggregate  (cost=23.93..23.93 rows=1 width=4)
   ->  Index Scan using fi on foo  (cost=0.00..23.92 rows=6 width=4)
         Index Cond: (i < 10)
(3 rows)

這是一個使用 EXPLAIN EXECUTE 顯示預備查詢的執行計畫的範例

PREPARE query(int, int) AS SELECT sum(bar) FROM test
    WHERE id > $1 AND id < $2
    GROUP BY foo;

EXPLAIN ANALYZE EXECUTE query(100, 200);

                                                       QUERY PLAN
-------------------------------------------------------------------​------------------------------------------------------
 HashAggregate  (cost=10.77..10.87 rows=10 width=12) (actual time=0.043..0.044 rows=10 loops=1)
   Group Key: foo
   Batches: 1  Memory Usage: 24kB
   ->  Index Scan using test_pkey on test  (cost=0.29..10.27 rows=99 width=8) (actual time=0.009..0.025 rows=99 loops=1)
         Index Cond: ((id > 100) AND (id < 200))
 Planning Time: 0.244 ms
 Execution Time: 0.073 ms
(7 rows)

當然,此處顯示的特定數字取決於所涉及資料表的實際內容。另請注意,由於規劃器改進,這些數字,甚至選定的查詢策略,可能會在 PostgreSQL 版本之間有所不同。此外,ANALYZE 命令使用隨機取樣來估算資料統計資料;因此,即使資料表中資料的實際分佈沒有改變,在重新執行 ANALYZE 後,成本估算仍有可能改變。

請注意,先前的範例顯示了 EXECUTE 中給定的特定參數值的 自訂計畫。我們可能也希望查看參數化查詢的通用計畫,可以使用 GENERIC_PLAN 來完成

EXPLAIN (GENERIC_PLAN)
  SELECT sum(bar) FROM test
    WHERE id > $1 AND id < $2
    GROUP BY foo;

                                  QUERY PLAN
-------------------------------------------------------------------​------------
 HashAggregate  (cost=26.79..26.89 rows=10 width=12)
   Group Key: foo
   ->  Index Scan using test_pkey on test  (cost=0.29..24.29 rows=500 width=8)
         Index Cond: ((id > $1) AND (id < $2))
(4 rows)

在本例中,剖析器正確地推斷出 $1$2 應與 id 具有相同的資料類型,因此,來自 PREPARE 的參數類型資訊的缺乏並不是問題。在其他情況下,可能需要明確指定參數符號的類型,這可以透過轉換它們來完成,例如

EXPLAIN (GENERIC_PLAN)
  SELECT sum(bar) FROM test
    WHERE id > $1::integer AND id < $2::integer
    GROUP BY foo;

相容性

SQL 標準中沒有定義 EXPLAIN 語句。

PostgreSQL 9.0 版之前使用以下語法,並且仍然支援

EXPLAIN [ ANALYZE ] [ VERBOSE ] statement

請注意,在此語法中,選項必須完全按照顯示的順序指定。

參見

ANALYZE

提交更正

如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表單來回報文件問題。