EXPLAIN 沉默的內容以及如何讓它說話

開發人員向 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"

但「從表格上」閱讀文本中的計劃非常困難且不清楚:

  • 顯示在節點中 按子樹資源求和
    也就是說,要了解執行特定節點花了多少時間,或者從表中讀取的數據從磁碟中讀取了多少數據,您需要以某種方式從另一個節點中減去一個節點
  • 需要節點時間 乘以循環
    是的,減法並不是必須在「頭部」完成的最複雜的操作 - 畢竟,執行時間表示為節點一次執行的平均值,並且可能有數百個節點
  • 好吧,所有這些加在一起使我們無法回答主要問題 - 那麼誰 “最薄弱的環節”?

當我們試圖向數百名開發人員解釋這一切時,我們意識到從外部來看,它看起來像這樣:

EXPLAIN 沉默的內容以及如何讓它說話

這意味著我們需要...

工具

在其中,我們試圖收集所有關鍵機制,以幫助根據計劃和要求理解「誰應該受到責備以及該做什麼」。 好吧,並與社區分享您的部分經驗。
滿足並使用—— 解釋.tensor.ru

計劃的可見性

當這個計劃看起來像這樣的時候,是不是很容易理解呢?

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

不好。

但像這樣, 以縮寫形式當關鍵指標分開後,就更清楚了:

EXPLAIN 沉默的內容以及如何讓它說話

但如果計畫更複雜,他就會出手相救 圓餅圖時間分佈 按節點:

EXPLAIN 沉默的內容以及如何讓它說話

好吧,對於最困難的選擇,他會急於提供幫助 進度表:

EXPLAIN 沉默的內容以及如何讓它說話

例如,在一些非常重要的情況下,一個計劃可能有多個實際根:

EXPLAIN 沉默的內容以及如何讓它說話EXPLAIN 沉默的內容以及如何讓它說話

結構線索

好吧,如果計劃的整個結構及其痛點已經擺好並且可見,為什麼不向開發商突出顯示並用“俄語”解釋它們呢?

EXPLAIN 沉默的內容以及如何讓它說話我們已經收集了幾十個這樣的推薦範本。

逐行查詢分析器

現在,如果將原始查詢疊加到分析的計劃上,您可以看到每個單獨的語句花費了多少時間 - 如下所示:

EXPLAIN 沉默的內容以及如何讓它說話

……或者甚至像這樣:

EXPLAIN 沉默的內容以及如何讓它說話

將參數替換到請求中

如果您不僅將請求「附加」到計劃,還「附加」了日誌 DETAIL 行中的參數,則還可以將其複製到以下選項之一:

  • 在請求中進行值替換
    用於在您的基礎上直接執行並進一步分析

    SELECT 'const', 'param'::text;
  • 透過 PREPARE/EXECUTE 進行值替換
    當可以忽略參數部分時,模擬調度程序的工作 - 例如,在處理分區表時

    DEALLOCATE ALL;
    PREPARE q(text) AS SELECT 'const', $1::text;
    EXECUTE q('param'::text);
    

計劃檔案

貼、分析、與同事分享! 這些計劃將保留存檔,您可以稍後返回: 解釋.tensor.ru/archive

但如果您不想讓其他人看到您的計劃,請不要忘記選取「不在存檔中發布」方塊。

在接下來的文章中,我將討論分析計畫時出現的困難和決策。

來源: www.habr.com

添加評論