pg_stat_statements 模組提供了一種跟蹤伺服器執行的所有 SQL 語句的規劃和執行統計資訊的方法。
必須透過將 pg_stat_statements 新增到 postgresql.conf 檔案中的 shared_preload_libraries 來載入該模組,因為它需要額外的共享記憶體。這意味著需要重啟伺服器才能新增或刪除該模組。此外,為了使模組能夠正常工作,必須啟用查詢識別符號的計算,如果 compute_query_id 設定為 auto 或 on,或者載入了任何計算查詢識別符號的第三方模組,則會自動完成此操作。
當 pg_stat_statements 處於活動狀態時,它會跟蹤伺服器所有資料庫的統計資訊。要訪問和操作這些統計資訊,該模組提供了 pg_stat_statements 和 pg_stat_statements_info 檢視,以及實用函式 pg_stat_statements_reset 和 pg_stat_statements。這些不是全域性可用的,但可以透過 CREATE EXTENSION pg_stat_statements 為特定資料庫啟用。
該模組收集的統計資訊可透過一個名為 pg_stat_statements 的檢視獲得。此檢視為每個資料庫 ID、使用者 ID、查詢 ID 的不同組合以及它是否為頂級語句(最多可跟蹤的獨立語句數量)包含一行。檢視的列在表 F.22 中顯示。
表 F.22. pg_stat_statements 列
列 型別 描述 |
---|
執行語句的使用者 OID |
執行語句的資料庫 OID |
如果查詢被執行為頂級語句,則為真(如果 pg_stat_statements.track 設定為 top,則始終為真) |
用於識別相同標準化查詢的雜湊碼。 |
代表性語句的文字 |
語句被規劃的次數(如果 pg_stat_statements.track_planning 已啟用,否則為零) |
規劃該語句所花費的總時間(以毫秒為單位)(如果 pg_stat_statements.track_planning 已啟用,否則為零) |
規劃該語句所花費的最短時間(以毫秒為單位)。如果 pg_stat_statements.track_planning 被停用,或者計數器使用 minmax_only 引數設定為 true 的 pg_stat_statements_reset 函式重置且自重置以來從未被規劃過,則此欄位將為零。 |
規劃該語句所花費的最長時間(以毫秒為單位)。如果 pg_stat_statements.track_planning 被停用,或者計數器使用 minmax_only 引數設定為 true 的 pg_stat_statements_reset 函式重置且自重置以來從未被規劃過,則此欄位將為零。 |
規劃該語句所花費的平均時間(以毫秒為單位)(如果 pg_stat_statements.track_planning 已啟用,否則為零) |
規劃該語句所花費時間的總體標準偏差(以毫秒為單位)(如果 pg_stat_statements.track_planning 已啟用,否則為零) |
語句執行的次數 |
執行該語句所花費的總時間(以毫秒為單位) |
執行該語句所花費的最短時間(以毫秒為單位),此欄位將為零,直到該語句在執行了使用 minmax_only 引數設定為 true 的 pg_stat_statements_reset 函式執行重置後首次執行。 |
執行該語句所花費的最長時間(以毫秒為單位),此欄位將為零,直到該語句在執行了使用 minmax_only 引數設定為 true 的 pg_stat_statements_reset 函式執行重置後首次執行。 |
執行該語句所花費的平均時間(以毫秒為單位) |
執行該語句所花費時間的總體標準偏差(以毫秒為單位) |
由該語句檢索或受影響的行總數 |
該語句共享塊快取命中的總數 |
該語句讀取的共享塊總數 |
該語句弄髒的共享塊總數 |
該語句寫入的共享塊總數 |
該語句本地塊快取命中的總數 |
該語句讀取的本地塊總數 |
該語句弄髒的本地塊總數 |
該語句寫入的本地塊總數 |
該語句讀取的臨時塊總數 |
該語句寫入的臨時塊總數 |
該語句讀取共享塊的總時間(以毫秒為單位)(如果 track_io_timing 已啟用,否則為零) |
該語句寫入共享塊的總時間(以毫秒為單位)(如果 track_io_timing 已啟用,否則為零) |
該語句讀取本地塊的總時間(以毫秒為單位)(如果 track_io_timing 已啟用,否則為零) |
該語句寫入本地塊的總時間(以毫秒為單位)(如果 track_io_timing 已啟用,否則為零) |
該語句讀取臨時檔案塊的總時間(以毫秒為單位)(如果 track_io_timing 已啟用,否則為零) |
該語句寫入臨時檔案塊的總時間(以毫秒為單位)(如果 track_io_timing 已啟用,否則為零) |
該語句生成的 WAL 記錄總數 |
該語句生成的 WAL 全頁映像總數 |
該語句生成的 WAL 總量(以位元組為單位) |
WAL 緩衝區變滿的次數 |
該語句 JIT 編譯的函式總數 |
該語句用於生成 JIT 程式碼的總時間(以毫秒為單位) |
函式被內聯的次數 |
該語句用於行內函數總時間(以毫秒為單位) |
該語句被最佳化的次數 |
該語句用於最佳化總時間(以毫秒為單位) |
程式碼被髮出的次數 |
該語句用於發出程式碼的總時間(以毫秒為單位) |
該語句 JIT 編譯的元組變形函式總數 |
該語句用於 JIT 編譯元組變形函式總時間(以毫秒為單位) |
計劃啟動的並行工作程序數 |
實際啟動的並行工作程序數 |
此語句開始收集統計資訊的時間 |
此語句開始收集最小/最大統計資訊的時間(欄位 min_plan_time、max_plan_time、min_exec_time 和 max_exec_time) |
出於安全原因,只有超級使用者和具有 pg_read_all_stats 角色的使用者才能檢視其他使用者執行的查詢的 SQL 文字和 queryid。但是,如果檢視已安裝到其資料庫中,其他使用者可以看到統計資訊。
可規劃的查詢(即 SELECT、INSERT、UPDATE、DELETE 和 MERGE)以及實用命令,只要它們的查詢結構在內部雜湊計算後相同,就會合併到一個 pg_stat_statements 條目中。通常,出於此目的,兩個查詢被認為是相同的,如果它們在語義上等效,但包含的字面常量值除外。
關於常量替換和 queryid 的以下詳細資訊僅在 compute_query_id 已啟用時適用。如果您使用外部模組計算 queryid,則應參考其文件瞭解詳細資訊。
當常量的某個值被忽略以匹配查詢與其他查詢時,該常量將在 pg_stat_statements 顯示中被替換為引數符號,例如 $1。查詢文字的其餘部分是與 pg_stat_statements 條目關聯的特定 queryid 雜湊值的第一個查詢的文字。
可以應用標準化的查詢可能在 pg_stat_statements 中以常量值顯示,尤其是在條目去分配率較高的情況下。為減少這種情況發生的可能性,請考慮增加 pg_stat_statements.max。pg_stat_statements_info 檢視(下面在 F.32.2. pg_stat_statements_info 檢視中討論)提供了關於條目去分配的統計資訊。
在某些情況下,具有明顯不同文字的查詢可能會合併到一個 pg_stat_statements 條目中;如上所述,這對於語義等效的查詢是預期發生的。此外,如果查詢之間唯一的區別是常量列表的元素數量,則該列表將被壓縮成一個元素,但會顯示一個帶註釋的列表指示符
=# SELECT pg_stat_statements_reset(); =# SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5, 6, 7); =# SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5, 6, 7, 8); =# SELECT query, calls FROM pg_stat_statements WHERE query LIKE 'SELECT%'; -[ RECORD 1 ]------------------------------ query | SELECT * FROM test WHERE a IN ($1 /*, ... */) calls | 2
除了這些情況之外,還存在雜湊衝突導致不相關查詢合併成一個條目的可能性。(然而,這不會發生在屬於不同使用者或資料庫的查詢之間。)
由於 queryid 雜湊值是在查詢的 post-parse-analysis 表示形式上計算的,因此反過來也是可能的:具有相同文字的查詢可能顯示為不同的條目,如果它們由於 search_path 設定不同等因素而具有不同的含義。
pg_stat_statements 的使用者可能希望使用 queryid(可能與 dbid 和 userid 結合使用)作為每個條目比其查詢文字更穩定可靠的識別符號。但是,重要的是要理解 queryid 雜湊值的穩定性僅有有限的保證。由於識別符號是從 post-parse-analysis 樹派生的,因此其值是諸如此表示形式中出現的內部物件識別符號等的函式。這會產生一些令人費解的影響。例如,pg_stat_statements 將認為兩個看起來相同的查詢是不同的,如果它們引用了在執行這兩個查詢之間被刪除並重新建立的函式。反之,如果一個表在查詢執行之間被刪除並重新建立,兩個看起來相同的查詢可能被認為是相同的。但是,如果對於其他類似的查詢,表的別名不同,則這些查詢將被視為不同的。雜湊過程對機器架構和其他平臺方面差異也很敏感。此外,不能安全地假設 queryid 在 PostgreSQL 的主要版本之間是穩定的。
基於物理 WAL 重放進行復制的兩個伺服器,對於同一個查詢,其 queryid 值應該是相同的。然而,邏輯複製方案並不保證副本在所有相關細節上保持一致,因此 queryid 不能作為跨多個邏輯副本累積成本的有用識別符號。如有疑問,建議進行直接測試。
通常,可以假定 queryid 值在 PostgreSQL 的次要版本釋出之間是穩定的,前提是例項在相同的機器架構上執行且目錄元資料細節匹配。相容性只有在萬不得已時才會破壞次要版本之間的相容性。
用於替換代表性查詢文字中常量的引數符號從原始查詢文字中最高的 $n 引數之後的下一個數字開始,如果沒有則為 $1。值得注意的是,在某些情況下可能存在隱藏的引數符號會影響此編號。例如,PL/pgSQL 使用隱藏的引數符號將函式區域性變數的值插入到查詢中,因此像 SELECT i + 1 INTO j 這樣的 PL/pgSQL 語句將具有像 SELECT i + $2 這樣的代表性文字。
代表性查詢文字儲存在外部磁碟檔案中,不佔用共享記憶體。因此,即使非常長的查詢文字也可以成功儲存。但是,如果積累了大量長查詢文字,外部檔案可能會變得過大而無法管理。如果發生這種情況,作為一種恢復方法,pg_stat_statements 可能會選擇丟棄查詢文字,此時 pg_stat_statements 檢視中的所有現有條目將顯示 null 的 query 欄位,但與每個 queryid 相關的統計資訊將保留。如果發生這種情況,請考慮減小 pg_stat_statements.max 以防止再次發生。
plans 和 calls 並不總是如預期那樣匹配,因為規劃和執行統計資訊是在各自的結束階段更新的,並且僅針對成功操作。例如,如果一個語句成功規劃但執行階段失敗,則僅更新其規劃統計資訊。如果由於使用了快取的計劃而跳過了規劃,則僅更新其執行統計資訊。
pg_stat_statements 模組本身的統計資訊透過一個名為 pg_stat_statements_info 的檢視進行跟蹤和提供。此檢視僅包含一行。檢視的列在表 F.23 中顯示。
表 F.23. pg_stat_statements_info 列
列 型別 描述 |
---|
由於觀察到的獨立語句數量超過 pg_stat_statements.max,最少執行語句的 pg_stat_statements 條目被去分配的總次數 |
pg_stat_statements 檢視中所有統計資訊上次重置的時間。 |
pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint, minmax_only boolean) returns timestamp with time zone
pg_stat_statements_reset 丟棄 pg_stat_statements 迄今為止收集的與指定的 userid、dbid 和 queryid 對應的統計資訊。如果任何引數未指定,則對每個引數使用預設值 0(無效),並且會重置與其他引數匹配的統計資訊。如果未指定任何引數或所有指定引數均為 0(無效),則會丟棄所有統計資訊。如果丟棄了 pg_stat_statements 檢視中的所有統計資訊,它還將重置 pg_stat_statements_info 檢視中的統計資訊。當 minmax_only 為 true 時,僅重置最小和最大規劃和執行時間的值(即 min_plan_time、max_plan_time、min_exec_time 和 max_exec_time 欄位)。minmax_only 引數的預設值為 false。上次執行的最小/最大重置時間顯示在 pg_stat_statements 檢視的 minmax_stats_since 欄位中。此函式返回重置的時間。此時間將儲存到 pg_stat_statements_info 檢視的 stats_reset 欄位或 pg_stat_statements 檢視的 minmax_stats_since 欄位(如果相應重置已實際執行)。預設情況下,此函式只能由超級使用者執行。可以使用 GRANT 授予其他人訪問許可權。
pg_stat_statements(showtext boolean) returns setof record
pg_stat_statements 檢視是透過一個同名的函式 pg_stat_statements 定義的。客戶端可以直接呼叫 pg_stat_statements 函式,並透過指定 showtext := false 來省略查詢文字(即,對應於檢視的 query 列的 OUT 引數將返回 null)。此功能旨在支援可能希望避免重複檢索不定長查詢文字開銷的外部工具。這類工具可以快取它們自己觀察到的每個條目的第一個查詢文字,因為 pg_stat_statements 本身就是這樣做的,然後僅在需要時檢索查詢文字。由於伺服器將查詢文字儲存在檔案中,因此這種方法可以減少重複檢查 pg_stat_statements 資料時的物理 I/O。
pg_stat_statements.max
(integer
) pg_stat_statements.max 是模組跟蹤的最大語句數(即,pg_stat_statements 檢視中的最大行數)。如果觀察到比此更多的獨立語句,則會丟棄關於最少執行語句的資訊。此類資訊被丟棄的次數可以在 pg_stat_statements_info 檢視中看到。預設值為 5000。此引數只能在伺服器啟動時設定。
pg_stat_statements.track
(enum
) pg_stat_statements.track 控制哪些語句被模組計數。指定 top 來跟蹤頂級語句(直接由客戶端發出的語句),all 來也跟蹤巢狀語句(例如函式內呼叫的語句),或 none 來停用語句統計資訊收集。預設值為 top。只有超級使用者可以更改此設定。
pg_stat_statements.track_utility
(boolean
) pg_stat_statements.track_utility 控制是否透過模組跟蹤實用命令。實用命令是除 SELECT、INSERT、UPDATE、DELETE 和 MERGE 之外的所有命令。預設值為 on。只有超級使用者可以更改此設定。
pg_stat_statements.track_planning
(boolean
) pg_stat_statements.track_planning 控制是否透過模組跟蹤規劃操作和持續時間。啟用此引數可能會帶來明顯的效能損失,尤其是在具有相同查詢結構的語句由許多併發連線執行時,這些連線會爭奪更新少量 pg_stat_statements 條目。預設值為 off。只有超級使用者可以更改此設定。
pg_stat_statements.save
(boolean
) pg_stat_statements.save 指定是否在伺服器關閉期間儲存語句統計資訊。如果為 off,則在關閉時不會儲存統計資訊,在伺服器啟動時也不會重新載入。預設值為 on。此引數只能在 postgresql.conf 檔案或伺服器命令列中設定。
該模組需要與 pg_stat_statements.max 成比例的額外共享記憶體。請注意,無論 pg_stat_statements.track 是否設定為 none,只要載入了該模組,就會消耗此記憶體。
這些引數必須在 postgresql.conf
中設定。典型的用法可能是
# postgresql.conf shared_preload_libraries = 'pg_stat_statements' compute_query_id = on pg_stat_statements.max = 10000 pg_stat_statements.track = all
bench=# SELECT pg_stat_statements_reset(); $ pgbench -i bench $ pgbench -c10 -t300 bench bench=# \x bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5; -[ RECORD 1 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2 calls | 3000 total_exec_time | 25565.855387 rows | 3000 hit_percent | 100.0000000000000000 -[ RECORD 2 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 calls | 3000 total_exec_time | 20756.669379 rows | 3000 hit_percent | 100.0000000000000000 -[ RECORD 3 ]---+-------------------------------------------------------------------- query | copy pgbench_accounts from stdin calls | 1 total_exec_time | 291.865911 rows | 100000 hit_percent | 100.0000000000000000 -[ RECORD 4 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 calls | 3000 total_exec_time | 271.232977 rows | 3000 hit_percent | 98.8454011741682975 -[ RECORD 5 ]---+-------------------------------------------------------------------- query | alter table pgbench_accounts add primary key (aid) calls | 1 total_exec_time | 160.588563 rows | 0 hit_percent | 100.0000000000000000 bench=# SELECT pg_stat_statements_reset(0,0,s.queryid) FROM pg_stat_statements AS s WHERE s.query = 'UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2'; bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5; -[ RECORD 1 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 calls | 3000 total_exec_time | 20756.669379 rows | 3000 hit_percent | 100.0000000000000000 -[ RECORD 2 ]---+-------------------------------------------------------------------- query | copy pgbench_accounts from stdin calls | 1 total_exec_time | 291.865911 rows | 100000 hit_percent | 100.0000000000000000 -[ RECORD 3 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 calls | 3000 total_exec_time | 271.232977 rows | 3000 hit_percent | 98.8454011741682975 -[ RECORD 4 ]---+-------------------------------------------------------------------- query | alter table pgbench_accounts add primary key (aid) calls | 1 total_exec_time | 160.588563 rows | 0 hit_percent | 100.0000000000000000 -[ RECORD 5 ]---+-------------------------------------------------------------------- query | vacuum analyze pgbench_accounts calls | 1 total_exec_time | 136.448116 rows | 0 hit_percent | 99.9201915403032721 bench=# SELECT pg_stat_statements_reset(0,0,0); bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5; -[ RECORD 1 ]---+----------------------------------------------------------------------------- query | SELECT pg_stat_statements_reset(0,0,0) calls | 1 total_exec_time | 0.189497 rows | 1 hit_percent | -[ RECORD 2 ]---+----------------------------------------------------------------------------- query | SELECT query, calls, total_exec_time, rows, $1 * shared_blks_hit / + | nullif(shared_blks_hit + shared_blks_read, $2) AS hit_percent+ | FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT $3 calls | 0 total_exec_time | 0 rows | 0 hit_percent |
Takahiro Itagaki <itagaki.takahiro@oss.ntt.co.jp>
。查詢標準化由 Peter Geoghegan <peter@2ndquadrant.com>
新增。
如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用此表單報告文件問題。