最近我告訴你如何使用標準食譜
#1. 切片
一篇關於如何以及為何值得組織的文章
「那些年的事…」
最初,和任何MVP 一樣,我們的專案在相當輕的負載下開始- 只對最關鍵的十台伺服器進行監控,所有表都相對緊湊......但隨著時間的推移,監控的主機數量越來越多,我們再次嘗試用其中一個做某事 表大小為 1.5TB,我們意識到雖然可以繼續這樣生活,但是很不方便。
那個時代幾乎就像史詩般的時代,不同版本的 PostgreSQL 9.x 是相關的,因此所有分區都必須「手動」完成 - 透過 表繼承和觸發器 動態路由 EXECUTE
.
事實證明,最終的解決方案足夠通用,可以轉換為所有表:
- 聲明了一個空的“header”父表,它描述了所有 必要的索引和觸發器.
- 從客戶端的角度來看的記錄是在「根」表中進行的,並且內部使用 路由觸發
BEFORE INSERT
該記錄被“物理”插入到所需的部分。 如果還沒有這樣的事情,我們就會捕獲一個異常並且... - … 透過使用
根據父表的範本創建 對所需日期有限制的部分這樣當檢索資料時,僅在其中執行讀取。CREATE TABLE ... (LIKE ... INCLUDING ...)
PG10:第一次嘗試
但從歷史上看,透過繼承進行分區並不適合處理活動寫入流或大量子分區。 例如,您可以回憶一下用於選擇所需部分的演算法 二次複雜度,它適用於 100 多個部分,您自己了解如何...
在PG10中,透過實施支援大大優化了這種情況
仔細閱讀手冊後發現,此版本中的本機分區表是:
- 不支援索引描述
- 不支援觸發器
- 不能成為任何人的“後代”
- 不支持
INSERT ... ON CONFLICT
- 無法自動生成節
額頭被耙子狠狠地敲了一下,我們意識到如果不修改應用程式就不可能做到這一點,因此將進一步的研究推遲了六個月。
PG10:第二次機會
於是,我們開始一一解決出現的問題:
- 因為觸發器和
ON CONFLICT
我們發現我們仍然到處需要它們,所以我們做了一個中間階段來解決它們 代理表. - 擺脫“路由” 在觸發器中 - 也就是說,從
EXECUTE
. - 他們分別拿出來 包含所有索引的範本表因此它們甚至不存在於代理表中。
最後,在這一切之後,我們對主表進行了原生分區。 新部分的創建仍然取決於應用程式的良心。
「鋸」字典
就像任何分析系統一樣,我們也有 “事實”與“刪減” (字典)。 在我們的案例中,他們以此身分行事,例如,
「事實」已經按天劃分很長時間了,所以我們平靜地刪除了過時的部分,它們並沒有打擾我們(日誌!)。 但是字典有問題...
並不是說有很多,但大約 100TB 的「事實」產生了 2.5TB 的字典。 您無法方便地從這樣的表中刪除任何內容,也無法在足夠的時間內壓縮它,並且寫入它的速度逐漸變慢。
就像一本字典......其中,每個條目應該只出現一次......這是正確的,但是!......沒有人阻止我們擁有 每天都有一本單獨的字典! 是的,這帶來了一定的冗餘,但它允許:
- 寫/讀速度更快 由於截面尺寸較小
- 消耗更少的記憶體 透過使用更緊湊的索引
- 儲存較少的數據 由於能夠快速刪除過時的
由於採取了一系列複雜的措施 CPU 負載降低約 30%,磁碟負載降低約 50%:
同時,我們繼續將完全相同的內容寫入資料庫,只是負載較少。
#2. 資料庫演化與重構
所以我們決定了我們所擁有的 每天都有自己的部分 與數據。 實際上, CHECK (dt = '2018-10-12'::date)
— 並且有一個分區鍵和一筆記錄落入特定部分的條件。
由於我們服務中的所有報告都是在特定日期的背景下建立的,因此自「非分區時間」以來它們的索引都是所有類型 (伺服器, 日期,計劃模板), (伺服器, 日期,計劃節點), (日期,錯誤類別,伺服器),...
但現在他們住在每個區域 你的副本 每個這樣的索引......以及在每個部分中 日期是一個常數……事實證明,現在我們在每個這樣的索引中 只要輸入一個常數 作為欄位之一,這會增加其數量和搜尋時間,但不會帶來任何結果。 他們把耙子留給了自己,哎呀......
優化的方向很明顯—簡單 從所有索引中刪除日期字段 在分區表上。 考慮到我們的數量,收益約為 1TB/週!
現在讓我們注意,這個太字節仍然必須以某種方式記錄。 也就是說,我們也 磁碟現在應該加載更少! 這張圖清楚地顯示了我們花了一周時間進行清潔所獲得的效果:
#3。 「分散」高峰負荷
負載系統的一大麻煩是 冗餘同步 一些不需要它的操作。 有時“因為他們沒有註意到”,有時“這樣更容易”,但遲早你必須擺脫它。
讓我們放大上一張圖片,看看我們有一個磁碟 雙振幅負載下的“泵浦” 相鄰樣本之間的差異,顯然「統計上」不應發生如此數量的操作:
這是很容易實現的。 我們已經開始監控 近1000台伺服器,每個都由一個單獨的邏輯線程處理,每個線程都會以一定的頻率重置要發送到資料庫的累積訊息,如下所示:
setInterval(sendToDB, interval)
這裡的問題恰恰在於: 所有執行緒幾乎同時啟動,因此他們的發送時間幾乎總是「恰到好處」地一致。 哎呀#2...
幸運的是,這很容易解決, 新增「隨機」助跑 按時間:
setInterval(sendToDB, interval * (1 + 0.1 * (Math.random() - 0.5)))
#4。 我們緩存我們需要的東西
第三個傳統的高負載問題是 無緩存 他在哪 可以 成為。
例如,我們可以根據計劃節點進行分析(所有這些 Seq Scan on users
),但立即認為它們在很大程度上是相同的 - 他們忘記了。
不,當然,沒有任何內容再次寫入資料庫,這會切斷觸發器 INSERT ... ON CONFLICT DO NOTHING
。 但這些數據仍然到達資料庫,而且沒有必要 閱讀以檢查衝突 得做。 哎呀#3...
啟用快取之前/之後傳送到資料庫的記錄數差異很明顯:
這是隨之而來的儲存負載下降:
在總
「每天太字節」聽起來很可怕。 如果你做的一切都是對的,那麼這只是 2^40 位元組/86400 秒 = ~12.5MB/s連桌面 IDE 螺絲也能固定。 🙂
但說實話,即使白天的負載「傾斜」十倍,您也可以輕鬆滿足現代 SSD 的功能。
來源: www.habr.com