應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

我建議你閱讀 Andrey Salnikov 於 2016 年初撰寫的報告“Typical Errors in applications that Lead to bloat in postgresql”

在本報告中,我將分析應用程式在設計和編寫應用程式程式碼階段出現的主要錯誤。 我將只考慮那些導致 Postgresql 膨脹的錯誤。 通常,這是整個系統效能結束的開始,儘管最初沒有可見的先決條件。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

很高興歡迎大家! 這份報告不像我同事的上一份報告那麼技術性。 本報告主要針對後端系統開發人員,因為我們有相當多的客戶。 他們都犯同樣的錯誤。 我會告訴你關於他們的事。 我將解釋這些錯誤會導致什麼致命和糟糕的事情。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

為什麼會犯錯? 這樣做有兩個原因:隨機的,也許它會起作用,並且由於不了解資料庫和應用程式之間以及資料庫本身之間發生的某些機制。

我將給你舉三個例子,並透過可怕的圖片來說明事情變得多麼糟糕。 我將簡要介紹那裡發生的機制。 以及如何處理它們,何時發生,以及採用什麼預防方法來防止錯誤發生。 我將向您介紹輔助工具並提供有用的連結。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

我使用了一個測試資料庫,其中有兩個表。 一個板塊包含客戶帳戶,另一個板塊包含這些帳戶上的交易。 我們會定期更新這些帳戶的餘額。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

碟的初始資料:很小,2MB。 資料庫和標誌的回應時間也非常好。 根據銘牌,負載相當不錯——每秒 2 次操作。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

透過這份報告,我將向您展示圖表,以便您可以清楚地了解正在發生的事情。 總會有兩張有圖表的投影片。 第一張投影片介紹了伺服器上通常發生的情況。

在這種情況下,我們看到我們確實有一個小標誌。 此索引很小,只有 2 MB。 這是左側第一張圖。

伺服器的平均回應時間也穩定且短。 這是右上方的圖表。

左下圖顯示最長的交易。 我們看到交易很快完成。 而且自動吸塵器在這裡還不起作用,因為這是一個啟動測試。 它將繼續發揮作用並對我們有用。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

第二張幻燈片將始終專用於正在測試的板。 在這種情況下,我們會不斷更新客戶的帳戶餘額。 我們看到更新操作的平均回應時間非常好,不到一毫秒。 我們看到處理器資源(這是右上圖)也被均勻消耗且相當小。

右下圖顯示了在更新之前我們為了搜尋所需的行而使用了多少操作和磁碟記憶體。 而根據符號的運算次數是每秒2次,如我一開始所說的。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

現在我們遇到了悲劇。 由於某種原因,有一筆長期被遺忘的交易。 原因通常都很平庸:

  • 最常見的情況之一是我們開始在應用程式程式碼中存取外部服務。 而這項服務並沒有回答我們。 也就是說,我們打開了一個事務,在資料庫中進行了更改,然後從應用程式讀取郵件或轉到我們基礎設施內的另一個服務,但由於某種原因,它沒有響應我們。 而我們的session就陷入了一個不知道什麼時候能解決的狀態。
  • 第二種情況是當我們的程式碼因為某些原因發生異常時。 在例外情況下,我們沒有處理交易的結束。 我們最終以懸而未決的交易結束了。
  • 最後一個也是相當常見的情況。 這是低品質的程式碼。 某些框架會開啟事務。 它掛起,您可能在應用程式中不知道它已掛起。

這些事情會導致什麼結果呢?

以至於我們的表和索引開始急劇膨脹。 這與膨脹效果完全相同。 對於資料庫來說,這將意味著資料庫回應時間將急劇增加,資料庫伺服器的負載將增加。 結果,我們的應用程式將受到影響。 因為如果您在程式碼中花費 10 毫秒來處理對資料庫的請求,在邏輯上花費 10 毫秒,那麼您的函數需要 20 毫秒才能完成。 而現在你的處境將會非常悲慘。

讓我們看看會發生什麼。 左下圖顯示我們有一筆長交易。 如果我們查看左上圖,我們會發現表的大小突然從 300 兆位元組躍升至 XNUMX 兆位元組。 同時,表中的資料量沒有變化,即存在相當多的垃圾。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

關於平均伺服器回應時間的總體情況也發生了幾個數量級的變化。 也就是說,伺服器上的所有請求開始完全下降。 同時,內部Postgres進程以autovacuum的形式啟動,這些進程正在嘗試做某事並消耗資源。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

我們的標誌怎麼了? 相同。 根據該標誌,我們的平均響應時間躍升了幾個數量級。 具體在消耗的資源方面,我們看到處理器的負載大大增加。 這是右上方的圖表。 它的增加是因為處理器必須對一堆無用的行進行排序以尋找所需的行。 這是右下圖。 結果,我們每秒的呼叫次數開始顯著下降,因為資料庫沒有時間處理相同數量的請求。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

我們需要恢復生機。 我們上網發現長時間的交易會導致問題。 我們找到並終止這筆交易。 對我們來說一切都變得正常了。 一切都按其應有的方式進行。

我們平靜下來,但過了一會兒我們開始注意到該應用程式的工作方式與緊急情況之前不同。 請求的處理速度仍然較慢,而且明顯較慢。 特別是在我的範例中,速度慢了一倍半到兩倍。 伺服器上的負載也比事故發生前更高。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

還有一個問題:“此時基地發生了什麼?” 且底座會出現以下情況。 交易圖表上可以看到已經停止了,確實沒有長期交易。 但事故期間該標誌的尺寸致命地增大了。 從那時起,它們就沒有減少。 基地的平均停留時間已經穩定。 答案似乎正在以我們可以接受的速度充分到來。 autovacuum 變得更加活躍,並開始對標誌執行某些操作,因為它需要篩選更多資料。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

具體來說,根據我們更改餘額的帳戶測試板:請求的回應時間似乎已恢復正常。 但實際上它高出一倍半。

並且從處理器上的負載來看,處理器上的負載還沒有恢復到崩潰之前所需的值。 原因就在右下圖。 可以看到那裡正在搜尋一定量的記憶體。 也就是說,為了找到所需的行,我們在對無用資料進行排序的同時浪費了資料庫伺服器的資源。 每秒的交易數量已經穩定。

整體來說不錯,但情況比以前更糟了。 由於我們的應用程式與該資料庫一起工作,因此清除了資料庫降級。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

為了了解其中發生的情況,如果您沒有看過先前的報告,現在讓我們來了解一些理論。 關於內部過程的理論。 為什麼要使用汽車吸塵器以及它的作用是什麼?

從字面上簡短地理解。 在某個時間點我們有一張桌子。 我們在表中有行。 這些線路可以是活躍的、有活力的,也是我們現在所需要的。 它們在圖片中被標記為綠色。 還有一些截止日期已經制定、更新,並且出現了新的條目。 並且它們被標記為資料庫不再對它們感興趣。 但由於 Postgres 的功能,它們出現在表中。

為什麼需要車用吸塵器? 在某個時刻,autovacuum 出現,訪問資料庫並詢問它:“請給我資料庫中目前打開的最舊交易的 ID。” 資料庫傳回該 id。 autovacuum 依靠它對表中的行進行排序。 如果他看到某些行被更舊的交易更改,那麼他有權將它們標記為我們將來可以透過在其中寫入新資料來重用的行。 這是一個後台進程。

這時候我們繼續操作資料庫,繼續對表格做一些修改。 在這些可以重複使用的行上,我們寫入新資料。 因此我們得到了一個循環,總是出現一些死的舊行,而不是我們寫下我們需要的新行。 這是 PostgreSQL 工作的正常狀態。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

事故期間發生了什麼事? 這個過程是如何發生的?

我們在某些​​情況下有一個標誌,有些是即時的,有些是截止日期。 汽車吸塵器已經到了。 他向資料庫詢問我們最舊的交易是什麼以及它的 id 是什麼。 我收到這個 ID,可能是幾個小時前,也許是十分鐘前。 這取決於資料庫的負載有多大。 他開始尋找可以標記為重複使用的線路。 我在我們的表中沒有找到這樣的行。

但此時我們繼續處理該表。 我們在其中做一些事情,更新它,更改資料。 這時候資料庫該做什麼呢? 她別無選擇,只能在現有表格的末尾添加新行。 因此我們的桌子尺寸開始膨脹。

事實上,我們需要綠線才能發揮作用。 但在這樣的問題中,事實證明,整個表中綠線的百分比非常低。

當我們執行查詢時,資料庫必須遍歷所有行:紅色和綠色,才能找到所需的行。 用無用資料使表膨脹的效果稱為“膨脹”,這也會耗盡我們的磁碟空間。 還記得嗎,原來是 2 MB,後來變成 300 MB了? 現在將兆位元組變更為千兆位元組,您將很快失去所有磁碟資源。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

可能會為我們帶來什麼後果?

  • 在我的範例中,表格和索引成長了 150 倍。 我們的一些客戶在磁碟空間不足時就遇到了更多致命案例。
  • 表本身的大小永遠不會減少。 在某些情況下,如果只有死行,Autovacuum 可能會切斷表格的尾部。 但由於不斷旋轉,一條綠線可能會凍結在末尾並且不會更新,而所有其他綠線將寫在圖板開頭的某個位置。 但這是一種不太可能發生的情況,以至於你的桌子本身的尺寸會縮小,所以你不應該指望它。
  • 資料庫需要對一大堆無用的行進行排序。 我們浪費磁碟資源、處理器資源和電力。
  • 這直接影響我們的應用程序,因為如果一開始我們在請求上花費了10 毫秒,在代碼上花費了10 毫秒,那麼在崩潰期間我們開始在請求上花費10 秒,在代碼上花費20 毫秒,即一個順序應用程式效能大幅下降。 當事故解決後,我們開始在一個請求上花費 10 毫秒,在一個代碼上花費 XNUMX 毫秒。 這意味著我們的生產力仍然下降了一倍半。 這一切都是因為一筆交易被凍結,也許是我們的錯。
  • 還有一個問題:「我們怎麼能恢復一切?」以便我們一切都好,並且請求會像事故發生前一樣快地收到。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

為此,需要執行一定的工作週期。

首先我們要找出有問題的臃腫的表。 我們了解到,在某些表中記錄較為活躍,而在另一些表中則不太活躍。 為此,我們使用擴展 pgstattuple。 透過安裝此擴展,您可以編寫查詢來幫助您找到非常臃腫的表。

找到這些表後,您需要壓縮它們。 已經有這方面的工具了。 在我們公司,我們使用三種工具。 第一個是內建 VACUUM FULL。 他殘忍、嚴厲、無情,但有時卻非常有用。 pg_repack и pg緊湊表 - 這些是用於壓縮表的第三方實用程式。 他們更仔細地對待資料庫。

它們的使用取決於什麼對您來說更方便。 但我會在最後告訴你這一點。 最主要的是有三個工具。 有很多可供選擇。

當我們糾正了一切並確保一切正常後,我們必須知道如何防止將來發生這種情況:

  • 它可以很容易地預防。 您需要監控主伺服器上的會話持續時間。 處於事務狀態空閒狀態的特別危險的會話。 這些人只是打開一個交易,做了一些事情然後離開,或者只是掛起,迷失在程式碼中。
  • 對於您來說,作為開發人員,在出現這些情況時測試您的程式碼非常重要。 這並不難做到。 這將是一個有用的檢查。 您將避免大量與長事務相關的“幼稚”問題。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

在這些圖中,我想向您展示在本例中使用 VACUUM FULL 進行符號檢查後,資料庫的符號和行為如何改變。 這不是我的生產。

表大小立即恢復到幾兆位元組的正常運作狀態。 這並沒有太大影響伺服器的平均回應時間。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

但特別是對於我們更新帳戶餘額的測試標誌,我們發現更新標誌中資料的請求的平均回應時間已縮短至緊急情況前的水平。 處理器完成此請求所消耗的資源也下降到崩潰前的水平。 右下圖顯示,現在我們立即準確地找到了所需的行,而無需遍歷表格壓縮之前存在的一堆死行。 平均請求時間保持在大致相同的水平。 但我的硬體出現了錯誤。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

第一個故事到這裡就結束了。 這是最常見的。 每個人都會遇到這種情況,無論客戶的經驗如何以及程式設計師的資格如何。 這種事遲早會發生。

第二個故事,我們分配負載並優化伺服器資源

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

  • 我們已經長大了,成為了認真的人。 我們知道我們有一個副本,這對我們平衡負載很有好處:寫入主伺服器,然後從副本讀取。 通常當我們要準備一些報告或ETL時,就會出現這種情況。 企業界對此非常高興。 他確實想要包含大量複雜分析的各種報告。
  • 報告需要花費很多小時,因為複雜的分析無法在毫秒內完成。 我們像勇敢的人一樣寫程式碼。 在插入應用程式中,我們在主伺服器上進行記錄,並在副本伺服器上執行報告。
  • 分配負載。
  • 一切都很完美。 我們很棒。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

而這種情況是什麼樣的呢? 具體來說,在這些圖表上,我還添加了來自副本的事務持續時間作為事務持續時間。 所有其他圖表僅指主伺服器。

這時,我的報告板已經長大了。 還有更多。 我們看到平均伺服器回應時間是穩定的。 我們看到在副本上有一個長時間運行的事務,運行了 2 小時。 我們看到 autovacuum 安靜地運行,它處理截止時間。 我們一切都很好。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

具體來說,根據測試的板塊,我們繼續更新那裡的帳戶餘額。 而且我們還有穩定的請求回應時間、穩定的資源消耗。 我們一切都很好。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

一切都很好,直到這些報告由於複製衝突而開始反擊。 他們會定期還擊。

我們上網並開始閱讀為什麼會發生這種情況。 我們找到了解決方案。

第一個解決方案是增加複製延遲。 我們知道我們的報告持續 3 小時。 我們將複製延遲設定為 3 小時。 我們正在推出一切,但仍然存在報告有時被取消的問題。

我們希望一切都完美。 我們爬得更遠。 我們在網路上發現了一個很酷的設定——hot_standby_feedback。 讓我們打開它。 Hot_standby_feedback 允許我們阻止 Master 上的 autovacuum。 這樣,我們就完全擺脫了複製衝突。 報告對我們來說一切都很順利。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

而此時Master伺服器發生了什麼事? 我們的主伺服器完全陷入困境。 現在,當我啟用這兩個設定時,我們可以看到圖表。 我們看到副本上的會話以某種方式開始影響主伺服器上的情況。 她確實有效果,因為她暫停了自動清理,從而清除了死線。 我們的桌子尺寸又猛增了。 整個資料庫的平均查詢執行時間也激增。 自動吸塵器收緊了一點。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

具體來說,從我們的板塊上,我們看到上面的資料更新也是一躍而上。 CPU消耗同樣大幅增加。 我們再次經歷大量死氣沉沉、無用的路線。 而且這個標誌的反應時間和交易數量都下降了。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

如果我們不知道我之前在說什麼,那會是什麼樣子?

  • 我們開始尋找問題。 如果我們在第一部分遇到問題,我們知道這可能是由於長時間的事務造成的,然後去找Master。 我們在Master上遇到了問題。 給他做香腸。 它開始升溫,其平均負載約為一百。
  • 那裡的請求很慢,但我們沒有看到任何長時間運行的事務。 我們不明白這是怎麼回事。 我們不知道該去哪裡找。
  • 我們檢查伺服器設備。 也許我們的襲擊失敗了。 也許我們的記憶棒燒壞了。 是的,任何事情都可能發生。 但不,伺服器是新的,一切正常。
  • 每個人都在奔跑:管理員、開發人員和主管。 沒有任何幫助。
  • 在某個時刻,一切突然開始自我修正。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

此時,我們副本上的請求已處理並離開。 我們收到了報告。 生意還是很愉快的。 正如你所看到的,我們的標誌又變大了,並且不會縮小。 在帶有會話的圖表上,我從副本中留下了這個長事務的一部分,以便您可以估計情況穩定下來需要多長時間。

會議結束了。 過了一段時間,伺服器才或多或少地恢復正常。 且Master伺服器上請求的平均回應時間恢復正常。 因為,最後,自動清理有機會清理並標記這些死線。 他開始做他的工作。 他做事的速度有多快,我們就能很快地恢復秩序。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

根據測試的平板電腦,我們在更新帳戶餘額時看到完全相同的圖片。 平均帳戶更新時間也逐漸正常化。 處理器消耗的資源也減少了。 並且每秒的交易數量恢復正常。 但我們再次恢復正常,與事故發生前不同。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

無論如何,我們都會遇到效能下降,就像第一種情況一樣,下降一倍半到兩倍,有時甚至更多。

我們似乎一切都做對了。 分配負載。 設備並沒有閒著。 我們按照自己的想法劃分了請求,但結果仍然很糟糕。

  • 不啟用hot_standby_feedback? 是的,如果沒有特別充分的理由,不建議打開它。 因為這種扭曲直接影響主伺服器並暫停那裡的 autovacuum 操作。 透過在某些副本上啟用它並忘記它,您可能會殺死主伺服器並導致應用程式出現大問題。
  • 增加 max_standby_streaming_delay? 是的,對於報道來說這是真的。 如果您有一個三小時的報告並且您不希望它由於複製衝突而崩潰,那麼只需增加延遲即可。 長期報告永遠不需要現在已到達資料庫的資料。 如果您擁有它三個小時,那麼您正在運行它一些舊的資料週期。 而對你來說,無論是延遲三小時或延遲六小時都沒有什麼差別,但你會持續收到報告,不會有任何掉落的問題。
  • 當然,您需要控制副本上的長會話,尤其是當您決定在副本上啟用 hot_standby_feedback 時。 因為任何事情都有可能發生。 我們將此副本提供給開發人員,以便他可以測試請求。 他寫了一個瘋狂的請求。 他啟動了它,然後去喝茶,我們就得到了既定的大師。 或者我們可能放錯了應用程式。 情況是多種多樣的。 副本上的會話必須像主伺服器一樣仔細監控。
  • 如果您對副本有快速且長時間的查詢,那麼在這種情況下最好將它們拆分以分配負載。 這是streaming_delay 的連結。 對於快速的副本,請擁有一個複製延遲較小的副本。 對於長時間運行的報告請求,請擁有可能延遲 6 小時或一天的副本。 這是完全正常的情況。

我們以同樣的方式消除後果:

  • 我們發現桌子臃腫。
  • 我們用適合我們的最方便的工具來壓縮它。

第二個故事到這裡就結束了。 我們繼續講第三個故事。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

對於我們進行遷移的人來說也很常見。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

  • 任何軟體產品都在成長。 對它的要求正在改變。 無論如何,我們都想發展。 碰巧我們需要更新表中的數據,即針對我們在開發過程中引入的新功能進行遷移更新。
  • 舊的資料格式並不令人滿意。 假設我們現在轉向第二個表,其中我有這些帳戶的交易。 假設它們以盧布為單位,我們決定提高準確性並以戈比為單位。 為此,我們需要進行更新:將欄位與交易金額乘以一百。
  • 在當今世界,我們使用自動化資料庫版本控制工具。 比方說 液體鹼。 我們在那裡登記我們的移民。 我們在我們的測試基地上對其進行測試。 一切都好。 更新正在進行中。 它會阻礙工作一段時間,但我們會得到更新的數據。 我們可以在此基礎上推出新功能。 一切都經過測試和檢查。 一切都得到了證實。
  • 我們進行了計劃工作並進行了遷移。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

這是呈現在您面前的遷移和更新。 由於這些是我的帳戶交易,因此該盤為 15 GB。 由於我們更新了每一行,因此我們透過更新將表的大小加倍,因為我們重寫了每一行。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

在遷移過程中,我們無法對此板執行任何操作,因為對它的所有請求都已排隊並等待此更新完成。 但在這裡我想提請您注意垂直軸上的數字。 也就是說,我們遷移前的平均請求時間約為 5 毫秒,且處理器負載、讀取磁碟記憶體的區塊操作數小於 7,5。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

我們進行了遷移,但又遇到了問題。

遷移成功,但是:

  • 舊功能現在需要更長的時間才能完成。
  • 桌子又變大了。
  • 伺服器上的負載再次變得比以前更大。
  • 當然,我們仍在修補那些運作良好的功能,我們對其進行了一些改進。

這又是膨脹,再次毀了我們的生活。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

在這裡,我演示了該表與前兩個案例一樣,不會恢復到先前的大小。 平均伺服器負載似乎足夠。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

如果我們轉向有帳戶的表,我們會看到該表的平均請求時間增加了一倍。 處理器的負載和記憶體中排序的行數躍升至 7,5 以上,但較低。 在處理器的情況下,它跳躍了 2 倍,在區塊操作的情況下,跳躍了 1,5 倍,也就是說,我們的伺服器效能下降了。 結果是——我們的應用程式的效能下降。 同時,來電數量大致保持在同一水平。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

這裡最主要的是了解如何正確地進行此類遷移。 他們需要完成。 我們非常一致地進行這些遷移。

  • 如此大規模的遷移不會自動發生。 它們必須始終處於控制之下。
  • 需要由有知識的人進行監督。 如果您的團隊中有 DBA,那麼就讓 DBA 來做。 這是他的工作。 如果沒有,那就讓最有經驗的人來做,他們知道如何使用資料庫。
  • 一個新的資料庫模式,即使我們更新一列,我們總是分階段準備,即在新版本應用程式推出之前提前準備:
  • 新增了新字段,我們將在其中記錄更新的資料。
  • 我們將資料分小部分從舊欄位傳輸到新欄位。 我們為什麼要這樣做呢? 首先,我們始終控制這個過程的過程。 我們知道我們已經轉移了這麼多批次,還剩下這麼多批次。
  • 第二個正面的影響是,在每個這樣的批次之間,我們關閉事務,打開一個新的事務,這允許自動清理根據盤子工作,標記重複使用的截止日期。
  • 對於應用程式運行時將出現的行(我們仍然運行舊應用程式),我們會新增一個觸發器,將新值寫入新欄位。 在我們的例子中,這是舊值乘以一百。
  • 如果我們非常頑固並且想要相同的字段,那麼在完成所有遷移並推出應用程式的新版本之前,我們只需重命名這些字段即可。 舊的領域被賦予了一些發明的名稱,新的領域被重新命名為舊的領域。
  • 之後我們才推出該應用程式的新版本。

同時我們不會變得臃腫,也不會在性能方面受到影響。

第三個故事到這裡就結束了。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat_approx.sql

現在詳細介紹一下我在第一個故事中提到的工具。

在搜尋 bloat 之前,必須安裝擴充功能 pgstattuple.

為了讓您不必提出查詢,我們已經在工作中編寫了這些查詢。 你可以使用它們。 這裡有兩個請求。

  • 第一個需要相當長的時間才能工作,但它會向您顯示表中的準確膨脹值。
  • 第二種工作速度較快,當你需要根據表格快速評估是否有膨脹時,非常有效。 您還應該了解 Postgres 表中始終存在膨脹。 這是其MVCC模型的一個特點。
  • 在大多數情況下,20% 的膨脹對於表來說是正常的。 也就是說,您不應該擔心並壓縮這個表。

我們找到如何辨識充滿無用資料的表。

現在關於如何修復膨脹:

  • 如果我們有一個小平板電腦和一個好的磁碟,也就是說,在一個千兆位元組的平板電腦上,很有可能使用 VACUUM FULL。 他會從你那裡拿走桌子上的獨佔鎖幾秒鐘,好吧,但他會快速而嚴厲地做所有事情。 真空完全有什麼作用? 它在表上取得獨佔鎖,並將舊表中的活動行重寫到新表中。 最後他取代了他們。 它刪除舊文件並用新文件替換舊文件。 但在其工作期間,它需要對錶進行獨佔鎖定。 這意味著您無法對該表執行任何操作:既不能寫入它,也不能讀取它,也不能修改它。 而VACUUM FULL需要額外的磁碟空間來寫入資料。
  • 下一個工具 pg_repack。 從原理上講,它與 VACUUM FULL 非常相似,因為它也是將舊文件中的資料重寫到新文件中並替換到表中。 但同時,它不會在工作一開始就對錶獲取獨佔鎖,而是僅在已經準備好資料以替換文件時才獲取它。 其磁碟資源要求與 VACUUM FULL 類似。 您需要額外的磁碟空間,如果您有 TB 表,這有時至關重要。 而且它非常需要處理器,因為它積極地使用 I/O。
  • 第三個實用程式是 pg緊湊表。 它對資源更加謹慎,因為它的工作原理略有不同。 pgcompacttable 的主要想法是,它使用表中的更新將所有活動行移至表的開頭。 然後它在此表上運行真空,因為我們知道開頭有活行,末尾有死行。 真空本身會切斷這個尾巴,即它不需要太多額外的磁碟空間。 同時,在資源方面仍可受到擠壓。

一切都有工具。

應用程式中導致 postgresql 膨脹的典型錯誤。 安德烈·薩爾尼科夫

如果您發現膨脹主題對進一步深入研究很有趣,這裡有一些有用的連結:

我更多地嘗試向開發人員展示一個恐怖故事,因為他們是我們資料庫的直接客戶,必須了解什麼以及會導致什麼操作。 我希望我成功了。 感謝您的關注!

問題

感謝您的報告! 您談到如何識別問題。 如何警告他們? 也就是說,我遇到了請求掛起的情況,不僅是因為它們存取了某些外部服務。 這些只是一些瘋狂的連結。 有一些微小的、無害的請求掛了一天,然後開始做一些無意義的事情。 也就是說,與您所描述的非常相似。 如何追蹤這個? 坐下來不斷觀察哪個請求被卡住了? 如何防止這種情況發生?

在這種情況下,這是公司管理員的任務,而不一定是 DBA 的任務。

我是管理員。

PostgreSQL 有一個名為 pg_stat_activity 的視圖,用於顯示懸空查詢。 你可以看到它掛在那裡多久。

我必須每 5 分鐘進來查看一次嗎?

設定 cron 並檢查。 如果您有長期要求,請寫一封信即可。 也就是說,你不需要用眼睛看,它可以自動化。 您將收到一封信,您對此做出反應。 或者你可以自動拍攝。

發生這種情況有什麼明顯的原因嗎?

我列出了一些。 其他更複雜的例子。 而且可以進行很長時間的對話。

感謝您的報告! 我想澄清一下 pg_repack 實用程式。 如果她不做獨佔鎖,那麼…

她做了排他鎖。

... 那我可能會丟失資料。 我的應用程式在這段時間內不應該記錄任何內容嗎?

不,它可以順利地與表配合使用,即 pg_repack 首先傳輸所有存在的即時行。 自然地,表中的某種條目會發生在那裡。 他只是把這個馬尾辮丟掉了。

也就是說,他最後真的做到了?

最後,他使用獨佔鎖來交換這些文件。

它會比 VACUUM FULL 更快嗎?

VACUUM FULL,一啟動就立刻拿了獨佔鎖。 在他完成一切之前,他不會放開她。 且 pg_repack 僅在檔案替換時取得獨佔鎖。 此時你不會在那裡寫入,但資料不會遺失,一切都會好起來的。

你好! 您談到了汽車吸塵器的操作。 有一張帶有紅色、黃色和綠色記錄單元的圖表。 也就是說,黃色的 - 他將它們標記為已刪除。 那麼,是不是可以寫入一些新的東西呢?

是的。 Postgres 不會刪除行。 他有這樣的特殊性。 如果我們更新了一行,我們會將舊行標記為已刪除。 更改此行的交易 ID 出現在那裡,我們寫入一個新行。 我們有可能會閱讀它們的會議。 到了某個時候,他們就變得很老了。 autovacuum 工作原理的本質是它遍歷這些行並將它們標記為不必要的。 您可以覆蓋那裡的數據。

我明白。 但這不是問題的重點。 我沒說完。 假設我們有一張桌子。 它具有可變大小的字段。 如果我嘗試插入新的東西,它可能根本不適合舊的單元格。

不,無論如何,整行都會在那裡更新。 Postgres 有兩種資料儲存模型。 它從資料類型中進行選擇。 有直接儲存在表中的數據,也有tos數據。 這些是大量數據:文字、json。 它們存儲在單獨的盤子中。 根據這些平板電腦,發生了與膨脹相同的故事,即一切都是一樣的。 它們只是單獨列出。

感謝您的報告! 使用語句超時查詢來限制持續時間是否可以接受?

非常可以接受。 我們到處都用這個。 由於我們沒有自己的服務,因此我們提供遠端支援,因此我們擁有各種各樣的客戶。 每個人都對此完全滿意。 也就是說,我們有 cron 作業來檢查。 會議的持續時間只是與客戶商定,在此之前我們不同意。 可能是一分鐘,也可能是十分鐘。 這取決於底座上的負載及其用途。 但我們都使用pg_stat_activity。

感謝您的報告! 我正在嘗試將您的報告應用到我的申請中。 看起來我們在任何地方都開始了一筆交易,並且在任何地方都清楚地完成了它。 如果出現異常,仍然會發生回滾。 然後我開始思考。 畢竟,交易可能不會明確啟動。 這大概是對女孩的暗示吧。 如果我只是更新一條記錄,交易是否會在 PostgreSQL 中啟動並僅在連接斷開時完成?

如果您現在談論應用程式級別,那麼它取決於您正在使用的驅動程式以及正在使用的 ORM。 那裡有很多設置。 如果啟用了自動提交,則交易將從此處開始並立即關閉。

也就是說,更新後就立即關閉?

這取決於設定。 我命名了一項設定。 這是自動提交。 這很常見。 如果啟用,則交易已開啟和關閉。 除非您明確地說“開始事務”和“結束事務”,而只是向會話中發起請求。

你好! 感謝您的報告! 假設我們有一個不斷膨脹的資料庫,然後伺服器上的空間耗盡了。 有什麼工具可以解決這種情況嗎?

需要正確監控伺服器上的空間。

例如,DBA 去喝茶、去度假村等。

建立檔案系統時,至少會在不寫入資料的地方創建某種備份空間。

如果它完全低於零怎麼辦?

在那裡它被稱為保留空間,即它可以被釋放,並且根據它創建的大小,您將獲得可用空間。 預設情況下我不知道有多少個。 在另一種情況下,交付磁碟以便您有空間執行重建操作。 您可以刪除一些您保證不需要的表。

還有其他工具嗎?

它總是手工製作的。 在本地,最好做什麼就變得很清楚,因為有些資料很關鍵,有些則不關鍵。 對於每個資料庫和與其配合使用的應用程序,這取決於業務。 它總是由當地決定。

感謝您的報告! 我有兩個問題。 首先,您顯示的投影片顯示當交易被卡住時,表空間大小和索引大小都會成長。 報告進一步介紹了一系列打包平板電腦的實用程式。 那指數呢?

他們也打包了。

但是真空不影響指數嗎?

有些使用索引。 例如,pg_rapack、pgcompacttable。 真空重新建立索引並影響它們。 VACUUM FULL 的想法是覆蓋一切,即它適用於每個人。

第二個問題。 我不明白為什麼有關副本的報告如此依賴複製本身。 在我看來,報告是讀取的,複製是寫入的。

是什麼導致複製衝突? 我們有一個發生過程的大師。 我們正在使用汽車吸塵器。 自動吸塵器實際上有什麼作用? 他正在剪掉一些舊台詞。 如果此時我們在副本上有一個讀取這些舊行的請求,並且在主伺服器上發生了 autovacuum 將這些行標記為可以覆蓋的情況,那麼我們就會覆蓋它們。 當我們收到一個資料包時,當我們需要在副本上重寫請求所需的那些行時,複製過程將等待您配置的逾時時間。 然後 PostgreSQL 將決定什麼對它來說更重要。 複製對他來說比請求更重要,他會發出請求以便在副本上進行這些更改。

安德烈,我有一個問題。 您在演示中展示的這些精彩圖表是您的某種實用工具的工作成果嗎? 圖表是如何製作的?

這是一項服務 歐克米特.

這是商業產品嗎?

是的。 這是一個商業產品。

來源: www.habr.com

添加評論