開發人員向 DBA 提出的經典問題或企業主向 PostgreSQL 顧問提出的經典問題聽起來幾乎總是相同的: “為什麼資料庫上的請求需要這麼長時間才能完成?”
傳統的原因集:
- 演算法效率低下
當您決定 JOIN 數萬筆記錄上的多個 CTE 時 - 過時的統計數據
如果表中資料的實際分佈已經與上次 ANALYZE 收集到的資料有很大差異 - 「插入」資源
CPU 不再有足夠的專用運算能力,千兆位元組的記憶體不斷被抽出,或者磁碟無法滿足資料庫的所有“需求” - 阻塞 來自競爭過程
如果阻塞很難捕捉和分析,那麼對於我們需要的其他一切 查詢計劃,可以使用以下方法獲得
但是,正如同一文檔中所述,
“理解計劃是一門藝術,掌握它需要一定的經驗…”
但如果您使用正確的工具,您也可以不用它!
查詢計劃通常是什麼樣的? 像這樣的東西:
Index Scan using pg_class_relname_nsp_index on pg_class (actual time=0.049..0.050 rows=1 loops=1)
Index Cond: (relname = $1)
Filter: (oid = $0)
Buffers: shared hit=4
InitPlan 1 (returns $0,$1)
-> Limit (actual time=0.019..0.020 rows=1 loops=1)
Buffers: shared hit=1
-> Seq Scan on pg_class pg_class_1 (actual time=0.015..0.015 rows=1 loops=1)
Filter: (relkind = 'r'::"char")
Rows Removed by Filter: 5
Buffers: shared hit=1
或者像這樣:
"Append (cost=868.60..878.95 rows=2 width=233) (actual time=0.024..0.144 rows=2 loops=1)"
" Buffers: shared hit=3"
" CTE cl"
" -> Seq Scan on pg_class (cost=0.00..868.60 rows=9972 width=537) (actual time=0.016..0.042 rows=101 loops=1)"
" Buffers: shared hit=3"
" -> Limit (cost=0.00..0.10 rows=1 width=233) (actual time=0.023..0.024 rows=1 loops=1)"
" Buffers: shared hit=1"
" -> CTE Scan on cl (cost=0.00..997.20 rows=9972 width=233) (actual time=0.021..0.021 rows=1 loops=1)"
" Buffers: shared hit=1"
" -> Limit (cost=10.00..10.10 rows=1 width=233) (actual time=0.117..0.118 rows=1 loops=1)"
" Buffers: shared hit=2"
" -> CTE Scan on cl cl_1 (cost=0.00..997.20 rows=9972 width=233) (actual time=0.001..0.104 rows=101 loops=1)"
" Buffers: shared hit=2"
"Planning Time: 0.634 ms"
"Execution Time: 0.248 ms"
但「從表格上」閱讀文本中的計劃非常困難且不清楚:
- 顯示在節點中 按子樹資源求和
也就是說,要了解執行特定節點花了多少時間,或者從表中讀取的數據從磁碟中讀取了多少數據,您需要以某種方式從另一個節點中減去一個節點 - 需要節點時間 乘以循環
是的,減法並不是必須在「頭部」完成的最複雜的操作 - 畢竟,執行時間表示為節點一次執行的平均值,並且可能有數百個節點 - 好吧,所有這些加在一起使我們無法回答主要問題 - 那麼誰 “最薄弱的環節”?
當我們試圖向數百名開發人員解釋這一切時,我們意識到從外部來看,它看起來像這樣:
這意味著我們需要...
工具
在其中,我們試圖收集所有關鍵機制,以幫助根據計劃和要求理解「誰應該受到責備以及該做什麼」。 好吧,並與社區分享您的部分經驗。
滿足並使用——
計劃的可見性
當這個計劃看起來像這樣的時候,是不是很容易理解呢?
Seq Scan on pg_class (actual time=0.009..1.304 rows=6609 loops=1)
Buffers: shared hit=263
Planning Time: 0.108 ms
Execution Time: 1.800 ms
不好。
但像這樣, 以縮寫形式當關鍵指標分開後,就更清楚了:
但如果計畫更複雜,他就會出手相救 圓餅圖時間分佈 按節點:
好吧,對於最困難的選擇,他會急於提供幫助 進度表:
例如,在一些非常重要的情況下,一個計劃可能有多個實際根:
結構線索
好吧,如果計劃的整個結構及其痛點已經擺好並且可見,為什麼不向開發商突出顯示並用“俄語”解釋它們呢?
我們已經收集了幾十個這樣的推薦範本。
逐行查詢分析器
現在,如果將原始查詢疊加到分析的計劃上,您可以看到每個單獨的語句花費了多少時間 - 如下所示:
……或者甚至像這樣:
將參數替換到請求中
如果您不僅將請求「附加」到計劃,還「附加」了日誌 DETAIL 行中的參數,則還可以將其複製到以下選項之一:
- 在請求中進行值替換
用於在您的基礎上直接執行並進一步分析SELECT 'const', 'param'::text;
- 透過 PREPARE/EXECUTE 進行值替換
當可以忽略參數部分時,模擬調度程序的工作 - 例如,在處理分區表時DEALLOCATE ALL; PREPARE q(text) AS SELECT 'const', $1::text; EXECUTE q('param'::text);
計劃檔案
貼、分析、與同事分享! 這些計劃將保留存檔,您可以稍後返回:
但如果您不想讓其他人看到您的計劃,請不要忘記選取「不在存檔中發布」方塊。
在接下來的文章中,我將討論分析計畫時出現的困難和決策。
來源: www.habr.com