PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

該報告提出了一些方法,使 每天有數百萬個 SQL 查詢時監控其效能,並且有數百台受監控的 PostgreSQL 伺服器。

什麼樣的技術解決方案可以讓我們有效率地處理如此大量的信息,這如何讓普通開發人員的生活變得更輕鬆?


誰有興趣? 具體問題分析與各種優化技術 SQL 查詢和解決 PostgreSQL 中的典型 DBA 問題 - 您還可以 閱讀一系列文章 關於這個話題。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)
我的名字是基里爾·博羅維科夫,我代表 張量公司。 具體來說,我在我們公司專門從事資料庫工作。

今天我將告訴你我們如何優化查詢,當你不需要「挑選」單一查詢的效能,而是整體解決問題時。 當有數百萬個請求時,您需要找到一些 解決方法 這個大問題。

一般來說,我們一百萬個客戶的張量是 VLSI 是我們的應用:企業社交網路、視訊通訊解決方案、內部和外部文件流程解決方案、會計和倉庫會計系統…也就是說,這樣一個用於綜合業務管理的“大型組合”,其中有100多個不同的內部專案.

為了確保它們都能正常運作和開發,我們在全國設有10個開發中心,還有更多 1000 名開發者.

我們從 2008 年就開始使用 PostgreSQL,累積了大量我們處理的資料——客戶端資料、統計、分析、來自外部資訊系統的資料—— 超過400TB。 光是生產環境就有大約 250 台伺服器,我們監控的資料庫伺服器總共大約有 1000 台。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

SQL 是一種聲明性語言。 你描述的不是某件事「如何」運作,而是你想要實現「什麼」。 DBMS 更清楚如何進行 JOIN - 如何連接表格、施加什麼條件、什麼將透過索引、什麼不會…

一些 DBMS 接受提示:“不,在這樣那樣的隊列中連接這兩個表”,但 PostgreSQL 無法做到這一點。 這是領先開發人員的自覺立場:“我們寧願完成查詢優化器,也不願允許開發人員使用某種提示。”

但是,儘管 PostgreSQL 不允許「外部」控制自己,但它完美地允許 看看他內心發生了什麼當您執行查詢時,以及哪裡出現問題。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

一般來說,開發人員(對於 DBA)通常會遇到哪些經典問題? 「在這裡我們滿足了要求,並且 我們一切都很慢,一切都懸而未決,有事正在發生……某種麻煩!”

原因幾乎總是相同的:

  • 低效的查詢演算法
    開發人員:“現在我透過 JOIN 在 SQL 中給他 10 個表...” - 並期望他的條件將奇蹟般地有效“解開”,並且他將快速獲得一切。 但奇蹟不會發生,任何具有這種可變性的系統(一個 FROM 中有 10 個表)總是會出現某種錯誤。 [文章]
  • 過時的統計數據
    這一點與 PostgreSQL 非常相關,當您將大型資料集「倒入」到伺服器上並發出請求時,它就會「sexcanits」您的平板電腦。 因為昨天裡面有10筆記錄,今天有10萬筆記錄,但是PostgreSQL還沒有意識到這一點,我們需要告訴它這一點。 [文章]
  • 「插入」資源
    您在一台沒有足夠磁碟、記憶體或處理器效能的薄弱伺服器上安裝了一個大型且負載很重的資料庫。 僅此而已...某個地方存在一個效能上限,您無法再跨越該上限。
  • 阻塞
    這是一個困難,但它們與各種修改查詢(INSERT、UPDATE、DELETE)最相關——這是一個單獨的大主題。

制定計劃

……對於其他一切,我們 需要一個計劃! 我們需要查看伺服器內部發生了什麼。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

PostgreSQL 的查詢執行計劃是用文字表示的查詢執行演算法的樹。 規劃者分析的結果正是該演算法被認為是最有效的。

每個樹節點都是一個操作:從資料表或索引檢索資料、建立位圖、連接兩個資料表、連接、相交或排除選擇。 執行查詢涉及遍歷該樹的節點。

要取得查詢計劃,最簡單的方法是執行語句 EXPLAIN。 要取得所有真實屬性,即在基礎上實際執行查詢 - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

不好的部分:當你運行它時,它發生在“此時此地”,所以它只適合本地偵錯。 如果您使用一台處於強資料變化流下的高負載伺服器,您會看到:「喔! 這裡我們的執行速度很慢 要求。” 半小時、一小時前 - 當您運行並從日誌中獲取此請求並將其帶回伺服器時,您的整個資料集和統計資料發生了變化。 您運行它來調試 - 它運行得很快! 而你無法理解為什麼,為什麼 慢慢地。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

為了了解請求在伺服器上執行的那一刻到底發生了什麼,聰明人寫道 自動解釋模組。 它存在於幾乎所有最常見的 PostgreSQL 發行版中,並且可以簡單地在設定檔中啟動。

如果它意識到某些請求的運行時間超過了您告訴它的限制,它就會執行 該請求的計劃的“快照”並將它們一起寫入日誌中.

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

現在一切似乎都很好,我們去日誌那裡看到... [文字腳布]。 但我們對此無話可說,只能說這是一個出色的計劃,因為執行時間為 11 毫秒。

一切似乎都很好——但究竟發生了什麼事卻不得而知。 除了一般時間之外,我們實際上看不到任何東西。 因為看這樣一隻純文字的「羔羊」一般是不直觀的。

但即使不明顯,即使不方便,還有更根本的問題:

  • 該節點表示 整個子樹的資源總和 在他之下。 也就是說,如果該特定索引掃描下存在某些嵌套條件,則您不能只找出該特定索引掃描花費了多少時間。 我們必須動態地查看內部是否有「子項」和條件變數、CTE,並減去「我們心中」的所有這些。
  • 第二點:節點上指示的時間為 單節點執行時間。 例如,如果該節點是由於多次循環表記錄而執行的,則計劃中的循環數(該節點的周期)會增加。 但原子執行時間本身在計畫方面保持不變。 也就是說,為了了解該節點總共執行了多長時間,您需要將一件事乘以另一件事 - 再次「在您的頭腦中」。

在這種情況下,要了解“誰是最弱的環節?” 幾乎不可能。 因此,就連開發者自己也在《手冊》中寫到: 「理解計劃是一門必須學習、體驗的藝術…”.

但我們有 1000 名開發人員,你無法將這種體驗傳達給每個人。 我、你、他都知道,但那邊有人已經不知道了。 也許他會學習,也可能不會,但他現在需要工作──他從哪裡獲得這種經驗?

計劃視覺化

因此,我們意識到,為了解決這些問題,我們需要 計劃的良好可視化. [文章]

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

我們先「瀏覽市場」——讓我們在網路上看看到底有什麼。

但事實證明,幾乎沒有相對「活躍」的解決方案或多或少地在開發中——從字面上看,只有一個: 解釋.depesz.com 作者:休伯特‧盧巴澤夫斯基。 當您在「feed」欄位中輸​​入計劃的文字表示形式時,它會向您顯示一個包含已解析資料的表格:

  • 節點自身的處理時間
  • 整個子樹的總時間
  • 檢索到的統計預期記錄數
  • 節點體本身

該服務還能夠共享連結存檔。 你把你的計劃放在那裡並說:“嘿,Vasya,這是一個鏈接,那裡出了點問題。”

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

但也存在一些小問題。

首先,大量的「複製貼上」。 你拿一塊木頭,把它插在那裡,一次又一次。

其次, 不分析讀取的資料量 — 輸出相同的緩衝區 EXPLAIN (ANALYZE, BUFFERS),我們在這裡看不到它。 他根本不知道如何拆解它們、理解它們並與它們一起工作。 當您讀取大量資料並意識到可能錯誤分配磁碟和記憶體快取時,此資訊非常重要。

第三個缺點是這個項目的發展非常薄弱。 提交量非常小,如果每六個月一次就好了,而且程式碼是用 Perl 寫的。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

但這都是“歌詞”,我們可以以某種方式接受這一點,但有一件事極大地讓我們遠離了這項服務。 這些是公共表表達式(CTE)和各種動態節點(如 InitPlan/SubPlan)分析中的錯誤。

如果你相信這張圖,那麼每個單獨節點的總執行時間就大於整個請求的總執行時間。 這很簡單 - 此 CTE 的產生時間未從 CTE 掃描節點中減去。 因此,我們不再知道 CTE 掃描本身需要多長時間的正確答案。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

然後我們意識到是時候寫我們自己的了——萬歲! 每個開發人員都會說:“現在我們要編寫自己的,這將非常簡單!”

我們採用了 Web 服務的典型堆疊:基於 Node.js + Express 的核心,使用 Bootstrap 和 D3.js 繪製漂亮的圖表。 我們的期望是完全合理的 - 我們在兩週內收到了第一個原型:

  • 自訂計劃解析器
    也就是說,現在我們可以從 PostgreSQL 產生的計劃中解析任何計劃。
  • 正確分析動態節點 - CTE 掃描、InitPlan、子計劃
  • 緩衝區分佈分析 - 資料頁從記憶體讀取,從本機快取讀取,從磁碟讀取
  • 清楚了
    這樣就不會在日誌中「挖掘」所有這些,而是立即在圖片中看到「最薄弱的環節」。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

我們得到了類似的東西,其中包括語法突出顯示。 但通常我們的開發人員不再使用完整的計劃表示,而是使用較短的計劃。 畢竟,我們已經解析了所有的數字,並把它們左右扔了,中間我們只留下了第一行,它是什麼樣的節點:CTE掃描,CTE生成或根據某種符號進行Seq掃描。

這就是我們所說的縮寫表示 計劃模板.

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

還有什麼會方便呢? 可以很方便地查看總時間的多少份額分配給哪個節點 - 只需將其“粘在一邊”即可 餅形圖.

我們指向該節點並查看 - 事實證明,Seq Scan 花費了不到總時間的四分之一,剩下的 3/4 是由 CTE Scan 花費的。 恐怖! 如果您在查詢中積極使用 CTE 掃描,這是關於 CTE 掃描「射速」的一個小註釋。 它們的速度不是很快——它們甚至不如常規的表格掃描。 [文章] [文章]

但通常這樣的圖表更有趣、更複雜,例如,當我們立即指向一個片段並看到超過一半的時間某些序列掃描「吃掉」時。 而且,裡面有某種Filter,很多記錄都根據它被丟棄了……你可以直接把這張圖扔給開發者說:「Vasya,這裡的一切對你來說都很糟糕! 弄清楚,看看——有什麼地方不對勁!”

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

當然,其中也牽涉到一些「耙子」。

我們遇到的第一件事是捨入問題。 計劃中每個單獨節點的時間以 1 μs 的精度指示。 當節點週期數超過(例如)1000 時,執行 PostgreSQL「在精度範圍內」除法後,計算回來時我們得到的總時間「在 0.95 毫秒到 1.05 毫秒之間」。 當計數達到微秒時,沒關係,但是當它已經是[毫秒]秒時,在將資源“解綁”到“誰消耗了多少”計劃的節點時,您必須考慮此資訊。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

第二點更複雜,是動態節點之間資源(那些緩衝區)的分配。 這花費了我們原型製作的前 2 週時間以及另外 4 週的時間。

遇到這種問題很容易——我們做了一個 CTE,並假設在其中讀到了一些東西。 事實上,PostgreSQL 很“聰明”,不會直接在那裡讀取任何內容。 然後我們從中取出第一個記錄,並將來自同一 CTE 的第一百零一個記錄放入其中。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

我們查看計劃並了解- 很奇怪,我們在Seq Scan 中「消耗」了3 個緩衝區(資料頁),在CTE 掃描中「消耗」了1 個緩衝區,在第二個CTE 掃描中又消耗了2 個緩衝區。 也就是說,如果我們簡單地將所有內容相加,我們將得到 6,但從平板電腦上我們只讀到 3! CTE Scan 不會從任何地方讀取任何內容,而是直接使用進程記憶體。 也就是說,這裡明顯有問題!

事實上,這就是Seq Scan請求的全部3頁數據,第1頁要求第1次CTE Scan,然後第2頁,還有2頁給他讀,也就是說,總共讀取了3 頁數據,而不是6 頁。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

這張圖讓我們認識到計劃的執行不再是一棵樹,而只是某種無環圖。 我們得到了這樣的圖表,以便我們了解「什麼最初來自哪裡」。 也就是說,這裡我們從pg_class創建了一個CTE,並且請求了兩次,而當我們第二次請求時,幾乎所有的時間都花在了分支上。 顯然,讀取第 2 個條目比僅從平板電腦讀取第 101 個條目要昂貴得多。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

我們一時鬆了口氣。 他們說:「現在,尼奧,你懂功夫了! 現在我們的體驗就在您的螢幕上。 現在你可以使用它了。” [文章]

日誌整合

我們的 1000 名開發人員鬆了一口氣。 但我們知道,我們只有數百台「戰鬥」伺服器,開發人員的所有這些「複製貼上」一點也不方便。 我們意識到我們必須自己收集。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

一般來說,有一個標準模組可以收集統計數據,但是,它也需要在配置中啟動 - 這個 pg_stat_statements 模組。 但他不適合我們。

首先,它在同一資料庫中使用不同的方案分配給相同的查詢 不同的QueryId。 也就是說,如果你先這樣做 SET search_path = '01'; SELECT * FROM user LIMIT 1;然後 SET search_path = '02'; 並且相同的請求,那麼該模組的統計數據將有不同的記錄,並且在不考慮方案的情況下,我將無法專門在該請求配置文件的上下文中收集一般統計數據。

阻止我們使用它的第二點是 缺乏計劃。 也就是說,沒有計劃,只有請求本身。 我們看到了什麼在放緩,但我們不明白為什麼。 在這裡,我們回到快速變化的資料集的問題。

還有最後一刻—— 缺乏“事實”。 也就是說,您無法解決查詢執行的特定實例 - 沒有,只有聚合統計資料。 儘管可以使用此方法,但非常困難。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

因此,我們決定反對複製貼上並開始寫作 集電極.

收集器透過 SSH 連接,使用憑證與資料庫建立安全連接,並且 tail -F 在日誌檔案中「堅持」它。 那麼在本次會議中 我們得到了整個日誌檔案的完整“鏡像”,由伺服器產生。 伺服器本身的負載很小,因為我們不解析那裡的任何內容,我們只是鏡像流量。

由於我們已經開始在 Node.js 中編寫接口,因此我們繼續在其中編寫收集器。 而且這項技術已經證明了自己的合理性,因為使用 JavaScript 來處理弱格式的文字資料(即日誌)非常方便。 Node.js 基礎設施本身作為後端平台,讓您可以輕鬆方便地使用網路連接,甚至任何資料流。

因此,我們「拉伸」兩個連接:第一個「監聽」日誌本身並將其帶給我們自己,第二個定期詢問基地。 “但是日誌顯示 oid 123 的標誌被阻止了”,但這對開發人員來說沒有任何意義,最好詢問資料庫“OID = 123 到底是什麼?” 因此,我們定期向基地詢問我們對自己尚不了解的情況。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

「只有一件事你沒有考慮到,有一種像大像一樣的蜜蜂!…」當我們想要監控 10 台伺服器時,我們開始開發這個系統。 我們理解中最關鍵的,出現了一些難以處理的問題。 但在第一季度,我們收到了一百個監控 - 因為系統有效,每個人都想要它,每個人都感到舒適。

這一切都需要加起來,數據流量大且活躍。 事實上,我們監控的、我們能處理的,就是我們使用的。 我們也使用 PostgreSQL 作為資料儲存。 沒有什麼比操作員更快地將數據「倒入」其中了 COPY 還沒有。

但簡單地「傾倒」數據並不是我們真正的技術。 因為如果一百台伺服器上每秒大約有 50k 個請求,那麼每天將產生 100-150GB 的日誌。 因此,我們必須仔細地“切割”底座。

首先,我們做了 按天劃分,因為總的來說,沒有人對日期之間的相關性感興趣。 如果今晚您推出了該應用程式的新版本 - 並且已經有了一些新的統計數據,那麼您昨天所擁有的有什麼不同呢?

其次,我們學到了(被迫) 使用起來非常非常快 COPY。 也就是說,不只是 COPY因為他比 INSERT,甚至更快。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

第三點——我不得不 分別放棄觸發器和外鍵。 也就是說,我們根本沒有參照完整性。 因為如果你有一個有一對 FK 的表,並且你在資料庫結構中說“這裡是 FK 引用的日誌記錄,例如一組記錄”,那麼當你插入它時,PostgreSQL一無所有,只剩下如何接受並誠實地去做 SELECT 1 FROM master_fk1_table WHERE ... 與您嘗試插入的標識符 - 只是為了檢查該記錄是否存在,並且您不會在插入時“破壞”該外鍵。

我們不再需要在目標表及其索引上新增一筆記錄,而是可以從它引用的所有表中讀取數據,從而獲得額外的好處。 但我們根本不需要這個——我們的任務是以最少的負載盡可能多、盡可能快地記錄。 所以 FK - 下來!

下一點是聚合和雜湊。 最初,我們在資料庫中實現它們 - 畢竟,當記錄到達時,立即在某種平板電腦中執行它很方便 觸發器中的“加一”。 嗯,這很方便,但同樣的壞事是——您插入一條記錄,但被迫從另一張表中讀取和寫入其他內容。 而且,你不只讀、寫,而且每次都這樣做。

現在假設您有一個表,您可以在其中簡單地​​計算透過特定主機傳遞的請求數: +1, +1, +1, ..., +1。 原則上,你不需要這個——一切都有可能 收集器記憶體中的總和 並一次發送到資料庫 +10.

是的,在出現某些問題的情況下,你的邏輯完整性可能會“崩潰”,但這幾乎是不現實的情況- 因為你有一個普通的伺服器,它的控制器中有一個電池,你有一個交易日誌,一個關於交易日誌的日誌。檔案系統...一般來說,不值得。 運行觸發器/FK 所造成的生產力損失不值得您所付出的費用。

這與散列相同。 某個請求飛到你面前,你從資料庫中計算出某個標識符,寫入資料庫,然後告訴大家。 一切都很好,直到在錄製時,第二個人來找你想要錄製同樣的東西 - 你被阻止了,這已經很糟糕了。 因此,如果可以將一些ID的生成轉移到客戶端(相對於資料庫),那麼最好這樣做。

對我們來說,使用文字中的 MD5 非常完美 - 請求、計劃、模板…我們在收集器端計算它,並將現成的 ID「倒入」資料庫中。 MD5的長度和每日分區讓我們不用擔心可能發生的衝突。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

但為了快速記錄這一切,我們需要修改記錄程式本身。

你通常如何寫入資料? 我們有某種資料集,我們將它分成幾個表,然後將其複製- 首先複製到第一個表,然後複製到第二個表,複製到第三個表......這很不方便,因為我們似乎分三步驟寫入一個資料流依序。 不愉快。 可以做得更快嗎? 能!

為此,只需將這些流彼此並行分解就足夠了。 事實證明,我們有錯誤、請求、模板、阻塞…在單獨的線程中飛行 - 並且我們並行編寫所有這些。 夠這個了 為每個單獨的目標表保持 COPY 通道持續打開.

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

也就是說,在收集器處 總有一股溪流,我可以在其中寫入我需要的資料。 但是為了讓資料庫看到這些數據,並且有人不會被困在等待這些數據被寫入, COPY 必須以一定的時間間隔中斷。 對我們來說,最有效的時間約為 100 毫秒 - 我們關閉它並立即再次打開同一張表。 如果我們在某些​​高峰期間沒有足夠的單一流量,那麼我們會匯集到一定的限制。

此外,我們發現對於這樣的負載配置文件,當批量收集記錄時,任何聚合都是有害的。 經典的邪惡是 INSERT ... VALUES 以及另外 1000 筆記錄。 因為此時媒體上存在寫入峰值,而嘗試向磁碟寫入內容的其他人都將等待。

要消除此類異常,只需不要聚合任何內容, 根本不緩衝。 如果確實發生了磁碟緩衝(幸運的是,Node.js 中的 Stream API 允許您找到答案) - 推遲此連接。 當您收到它再次空閒的事件時,請從累積佇列中寫入它。 當它很忙時,從池中取出下一個空閒的並寫入它。

在引入這種資料記錄方法之前,我們有大約 4K 的寫入操作,這樣我們就減少了 4 倍的負載。 現在,由於新的受監控資料庫,它們又增長了 6 倍 - 高達 100MB/s。 現在我們儲存過去 3 個月的日誌,容量約為 10-15TB,希望在短短三個月內任何開發人員都能夠解決任何問題。

我們了解問題所在

但僅僅收集所有這些數據是好的、有用的、相關的,但還不夠——它需要被理解。 因為每天有數百萬個不同的計劃。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

但數以百萬計的人難以管理,我們必須先做「小」。 首先,您需要決定如何組織這個「較小」的事情。

我們確定了三個關鍵點:

  • 發送了此請求
    也就是說,它是從什麼應用程式「到達」的:網路介面、後端、支付系統或其他東西。
  • 哪裡 它發生了
    在什麼具體伺服器上? 因為如果你在一個應用程式下有多個伺服器,突然其中一個“變得愚蠢”(因為“磁碟損壞”,“記憶體洩漏”,其他一些問題),那麼你需要專門解決該伺服器。
  • 作為 問題以這樣或那樣的方式表現出來

為了了解「誰」向我們發送了請求,我們使用標準工具 - 設定會話變數: SET application_name = '{bl-host}:{bl-method}'; — 我們發送發出請求的業務邏輯主機的名稱,以及發起請求的方法或應用程式的名稱。

在我們傳遞請求的“所有者”之後,它必須輸出到日誌 - 為此我們配置變數 log_line_prefix = ' %m [%p:%v] [%d] %r %a'。 對於那些有興趣的人來說,也許 看手冊這是什麼意思呢。 事實證明,我們在日誌中看到:

  • 時間
  • 流程和交易標識符
  • 資料庫名稱
  • 發送此請求的人的IP
  • 和方法名稱

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

然後我們意識到,查看不同伺服器之間一個請求的相關性並不是很有趣。 一個應用程式到處都出現同樣問題的情況並不常見。 但即使是相同的,也請查看這些伺服器中的任何一個。

所以這是剪輯 “一台伺服器 - 一天” 事實證明,這對我們進行任何分析來說都足夠了。

第一個分析部分是相同的 “樣本” - 計劃的簡化形式,清除所有數字指標。 第二個切點是應用或方法,第三個切點是為我們帶來問題的具體計畫節點。

當我們從特定實例轉向模板時,我們立即獲得了兩個優勢:

  • 分析對象數量倍增
    我們必須不再透過數千個查詢或計劃來分析問題,而是透過數十個模板來分析問題。
  • 時間軸
    也就是說,透過總結某個部分中的“事實”,您可以顯示它們在白天的外觀。 在這裡你可以理解,如果你有某種模式發生,例如,每小時一次,但它應該每天發生一次,你應該思考哪裡出了問題 - 誰造成了它以及為什麼,也許它應該在這裡不應該。 這是另一種非數字的、純粹視覺的分析方法。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

其餘的方法基於我們從計劃中提取的指標:這種模式發生了多少次,總時間和平均時間,從磁碟讀取了多少數據,以及從內存中讀取了多少數據......

因為,例如,您來到主機的分析頁面,看看 - 有些東西開始在磁碟上讀取太多資料。 伺服器上的磁碟無法處理它 - 誰從中讀取?

您可以按任何列進行排序,並決定您現在要處理的內容 - 處理器或磁碟上的負載,或請求總數...我們對其進行了排序,查看了“頂部”的內容,修復了它並推出了該應用程式的新版本。
【視訊講座】

您可以立即看到來自請求的具有相同模板的不同應用程序,例如 SELECT * FROM users WHERE login = 'Vasya'。 前端、後端、處理...並且您想知道如果用戶不與用戶交互,為什麼處理會讀取用戶。

相反的方法是立即從應用程式中看到它做了什麼。 例如,前端是這個、這個、這個、這個每小時一次(時間軸有幫助)。 問題立即出現:每小時做一次某事似乎不是前端的工作...

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

一段時間後,我們意識到我們缺乏聚合 按計劃節點統計。 我們僅將那些對錶本身的資料執行某些操作的節點與計劃隔離(透過索引或不透過索引讀取/寫入它們)。 其實相對上一張圖只增加了一個面向—— 這個節點為我們帶來了多少筆記錄?,以及有多少被丟棄(透過過濾器刪除的行)。

你的碟上沒有合適的索引,你向它發出請求,它飛過索引,陷入 Seq Scan...你已經過濾掉了除一條之外的所有記錄。 為什麼每天需要100M過濾記錄,把索引匯總起來不是更好嗎?

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

在逐個節點分析了所有計劃後,我們意識到計劃中的一些典型結構很可能看起來很可疑。 最好告訴開發人員:「朋友,這裡你首先按索引讀取,然後排序,然後切斷」 - 通常,有一條記錄。

每個編寫查詢的人可能都遇到過這種模式:「給我 Vasya 的最後一個訂單,它的日期。」如果您沒有按日期排列的索引,或者您使用的索引中沒有日期,那麼您將踩一模一樣的「耙子」。

但我們知道這是一個「耙子」——所以為什麼不立即告訴開發人員他應該做什麼。 因此,當現在打開一個計劃時,我們的開發人員立即看到一張帶有提示的漂亮圖片,他們立即告訴他:“你到處都有問題,但它們是這樣或那樣解決的。”

結果,一開始和現在解決問題所需的經驗量大幅下降。 這就是我們擁有的工具。

PostgreSQL 查詢的大量最佳化。 基里爾·博羅維科夫(張量)

來源: www.habr.com

添加評論