1 基礎知識
重點:
如果您的數據庫運行了很久,並且從來沒有打開過autovacuum,那么請在打開autovacuum之前全庫手動運行vacuum analyze(可能要非常久的時間)
完全禁用autovacuum,請不要這樣做,除非你真的知道你在做什么,並且需要定期清理腳本.否則當問題發生時你將不得不處理花費大量的時間處理,甚至可能需要停庫、停機
1.1 dead tuples
tuple:元組,也就是一行數據
首先,簡要解釋什么是"死元組"和"膨脹".
當您在PostgreSQL中執行DELETE時,行不會立即從數據文件中刪除.而是僅通過在頁頭中設置xmax字段將其標記為已刪除.同樣對於UPDATE,它可能在PostgreSQL中被視為DELETE+INSERT.
這是PostgreSQL MVCC背后的基本思想之一,因為它允許更大並發,在不同的進程之間最小的鎖定.這個MVCC實現的缺點是留下了已刪除的元組,即使在所有可能看到這些版本的事務完成之后也是如此.
如果沒有清理,那些"死元組"(對於任何事務實際上是不可見的)將永遠留在數據文件中.對於DELETE和UPDATE比較多的的表,死元組可能占據很多磁盤空間.同時,死元組也將從索引中引用,進一步增加了浪費的磁盤空間量.這就是我們在PostgreSQL中稱之為“膨脹”的東西,同時因為查詢也會變慢。
select n_dead_tup as "死元組數", (case when n_live_tup > 0 then n_dead_tup::float8/n_live_tup::float8 else 0 end) as "死/活元組的比例" from pg_stat_all_tables
1.2 vacuum和autovacuum
1.2.1 vacuum
回收死元組占用空間的最直接方法是手動運行VACUUM命令.此維護命令將掃描表並從表和索引中刪除死元組.它通常不會將磁盤空間返回到操作系統,但它將使其可用於新行.
注意:VACUUM FULL會回收空間並將其返回給操作系統,但是有許多缺點.首先,它將獲取表上的獨占鎖,阻止所有操作(包括SELECT).其次,它實際上創建了一個表的副本(復制了一個表),使所需的磁盤空間加倍,同時復制表非常慢.
VACUUM的問題在於它完全是手動操作.只有在您決定運行它時才會發生,而不是在需要時.您可以將它放入cron並在所有表上每5分鍾運行一次,但大多數表實際上不會清理任何內容,同時會使您的CPU和I/O使用率比較高.或者你可以每天只運行一次,在這種情況下,可能會累積很多的死元組.
上述問題可以使用autovacuum按需清理,以控制浪費的資源.數據庫知道隨着時間的推移產生了多少死元組(每個事務報告它刪除和更新的元組數),因此當表累積一定數量的死元組時會觸發清理(默認情況下這是20%的死元組)表,但是它會使用數據庫更繁忙,而在大部份數據庫空閑時間較少.
VACUUM語法結構:
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
Full Vacuum
full vacuum與單純的vacuum還是有很大的區別的。vacuum只是將刪除狀態的空間釋放掉,轉換到能夠重新使用的狀態,但是對於系統來說該數據塊的空閑空間並沒有反應到系統的元數據中。類似oracle中高水位標記並沒有下降。Full vacuum將會使空間釋放的信息表現在系統級別,其實質是將當前刪除記錄后面的數據進行移動,使得整體的記錄連貫起來,降低了“高水位標記”。
Vacuum analyze
analyze的功能是更新統計信息,使得優化器能夠選擇更好的方案執行sql。oracle中同樣也有analyze,作用也相同,目前更多的使用的是dbms_stats包。統計信息收集和更新對於系統性能來說非常重要,與oracle維護類似,通常可以通過采用手動或者定制任務的方式。也有不同,oracle在進行imp后自動的對相應數據對象進行統計信息的收集和更新,而postgresql的恢復過程還沒有集成到里面,需要手動去執行。
1.2.2 autoanalyze
清除死元組不是autovacuum的唯一任務,它還負責更新優化程序在規划查詢時使用的數據分布統計信息.您可以通過手動運行ANALYZE收集它們,但它遇到類似VACUUM問題,您可能經常或不經常運行它.
解決方案也類似,數據庫可以監視表中更改的行數,並自動運行ANALYZE.
注意:ANALYZE的效率稍差,因為VACUUM的成本與死元組的數量成正比(當很少/沒有時成本比較低),ANALYZE必須在每次執行時從頭開始重建統計數據.另一方面,如果你沒有經常運行它,那么執行時選擇的成本會嚴重影響執行效率。
show autovacuum 檢查autovacuum 是否開啟;
1.2.3 vacuum具體功能
1.回收空間
這個通常是大家最容易想起來的功能。回收空間,將dead tuple清理掉。但是已經分配的空間,一般不會釋放掉。除非做vacuum full,但是需要exclusive lock。一般不太建議,因為如果表最終還是會漲到
這個高水位上,經常做vacuum full意義不是非常大。一般合理設置vacuum參數,進行常規vacuum也就夠了。
2.凍結tuple的xid
PG會在每條記錄(tuple)的header中,存放xmin,xmax信息(增刪改事務ID)。transactionID的最大值為2的32次,即無符整形來表示。當transactionID超過此最大值后,會循環使用。
這會帶來一個問題:就是最新事務的transactionID會小於老事務的transactionID。如果這種情況發生后,PG就沒有辦法按transactionID來區分事務的先后,也沒有辦法實現MVCC了。因此PG用vacuum后台進程,
按一定的周期和算法觸發vacuum動作,將過老的tuple的header中的事務ID進行凍結。凍結事務ID,即將事務ID設置為“2”(“0”表示無效事務ID;“1”表示bootstrap,即初始化;“3”表示最小的事務ID)。PG認為被凍結的事務ID比任何事務都要老。這樣就不會出現上面的這種情況了。
3.更新統計信息
vacuum analyze時,會更新統計信息,讓PG的planner能夠算出更准確的執行計划。autovacuum_analyze_threshold和autovacuum_analyze_scale_factor參數可以控制analyze的觸發的頻率。
vacuum analyze 這將清理整個庫。
vacuum analyze table_name 指定表。
4.更新visibility map
在PG中,有一個visibility map用來標記那些page中是沒有dead tuple的。這有兩個好處,一是當vacuum進行scan時,直接可以跳過這些page。二是進行index-only scan時,可以先檢查下visibility map。這樣減少fetch tuple時的可見性判斷,從而減少IO操作,提高性能。另外visibility map相對整個relation,還是小很多,可以cache到內存中。
1.2.4 vacuum參數
- autovacuum:默認為on,表示是否開起autovacuum。默認開起。特別的,當需要凍結xid時,盡管此值為off,PG也會進行vacuum。
- autovacuum_naptime:下一次vacuum的時間,默認1min。 這個naptime會被vacuum launcher分配到每個DB上。autovacuum_naptime/num of db。
- log_autovacuum_min_duration:記錄autovacuum動作到日志文件,當vacuum動作超過此值時。 “-1”表示不記錄。“0”表示每次都記錄。
- autovacuum_max_workers:最大同時運行的worker數量,不包含launcher本身。
- autovacuum_vacuum_threshold:默認50。與autovacuum_vacuum_scale_factor配合使用, autovacuum_vacuum_scale_factor默認值為20%。當update,delete的tuples數量超過autovacuum_vacuum_scale_factor*table_size+autovacuum_vacuum_threshold時,進行vacuum。如果要使vacuum工作勤奮點,則將此值改小。
- autovacuum_analyze_threshold:默認50。與autovacuum_analyze_scale_factor配合使用, autovacuum_analyze_scale_factor默認10%。當update,insert,delete的tuples數量超過 autovacuum_analyze_scale_facto r* table_size + autovacuum_analyze_threshold時,進行analyze。
- autovacuum_freeze_max_age和autovacuum_multixact_freeze_max_age:前面一個200 million,后面一個400 million。離下一次進行xid凍結的最大事務數。
- autovacuum_vacuum_cost_delay:如果為-1,取vacuum_cost_delay值。
- autovacuum_vacuum_cost_limit:如果為-1,到vacuum_cost_limit的值,這個值是所有worker的累加值。
基於代價的vacuum參數:
- vacuum_cost_delay :計算每個毫秒級別所允許消耗的最大IO,vacuum_cost_limit/vacuum_cost_dely。 默認vacuum_cost_delay為20毫秒。
- vacuum_cost_page_hit :vacuum時,page在buffer中命中時,所花的代價。默認值為1。
- vacuum_cost_page_miss:vacuum時,page不在buffer中,需要從磁盤中讀入時的代價默認為10。 vacuum_cost_page_dirty:當vacuum時,修改了clean的page。這說明需要額外的IO去刷臟塊到磁盤。默認值為20。
- vacuum_cost_limit:當超過此值時,vacuum會sleep。默認值為200。
1.3 Throttling
autovacuum是在后台運行的維護任務,對用戶查詢的影響較小,換句話說,它不消耗太多資源(CPU和I/O),這正是autovacuum Throttling.
清理過程相當簡單,它從數據文件中讀取頁面(默認8kB數據塊),並檢查它是否需要清理.如果沒有死元組,頁面就會被丟棄而不做任何更改.否則它被清理(死元組被刪除),被標記為"臟"並最終寫出來.成本核算基於postgresql.conf定義三個參數:
vacuum_cost_page_hit = 1 #如果頁面是從shared_buffers讀取的,則計為1 vacuum_cost_page_miss = 10 #如果在shared_buffers找不到並且需要從操作系統中讀取,則計為10(它可能仍然從RAM提供,但我們不知道) vacuum_cost_page_dirty = 20 #當清理修改一個之前干凈的塊時需要花費的估計代價.它表示再次把臟塊刷出到磁盤所需要的額外I/O,默認值為20
根據以下參數我們可以計算autovacuum完成的"工作成本".然后通過autovacuum_vacuum_cost_limit可以一次完成的清理工作,默認情況下設置為200,每次清理完成后它將睡眠20ms:
autovacuum_vacuum_cost_delay = 20ms autovacuum_vacuum_cost_limit = 200
延遲20ms,清理可以每秒進行50輪,每輪200,每秒10000.這意味着:
從shared_buffers讀取80MB/s(假設沒有臟頁,10000/1*8k)
從OS讀取8MB/s(可能來自磁盤,10000/10*8k)
4 MB / s寫入(由autovacuum進程弄臟的頁面,10000/20*8k)
可以根據硬件的配置,以及autovacuum主要是順序讀寫的情況增加autovacuum_vacuum_cost_limit參數,例如增加到1000(或2000),這會使吞吐量增加5倍(或10倍).當然可以調整其他參數(每頁操作成本,睡眠延遲),但通常不建議這樣做,改變成本限制就足夠了.
2 調整目標
清理死元組:不浪費不合理的磁盤空間,防止索引膨脹並快速查詢;
最大限度地減少清理時帶來的影響:不要經常執行清理,因為它會浪費資源(CPU、I/O、RAM).並可能會嚴重影響性能。也就是說需要找到合適的平衡,經常運行它可能與不經常運行它一樣糟糕,這在很大程度上取決於您管理的數據量,您正在處理的工作負載類型(DELETE/UPDATE的數量).
postgresql.conf中的大多數默認值都非常保守,原因有兩個.首先默認值是在幾年前根據當時常見的資源(CPU、RAM等)決定的.其次默認配置可以在任何地方使用,對於許多部署(特別是較小的部署和/或處理讀取主要的工作負載),默認配置參數將正常工作.
隨着數據庫寫入量的增加,問題開始出現,典型的問題是從來不清理或較少清理,時間久了會顯著破壞數據庫性能,因為它必須處理大量垃圾.此時需要調整參數以便更頻繁地進行清理,並且每次處理較少量的死元組.
3 閾值和比例因子
autovacuum會受到兩個參數的影響 autovacuum_vacuum_threshold = 50 #閾值 autovacuum_vacuum_scale_factor = 0.2 #比例因子
每當死元組的數量(你可以看作pg_stat_all_tables.n_dead_tup)超過時就會觸發清理,公式為:
autovacuum_vacuum_threshold + pg_class.reltuples * autovacuum_vacuum_scale_factor
當滿足上面的公式時,該表將被視為需要清理.該公式基本上表示在清理之前,高達20%的表可能是死元組(50行的閾值是為了防止非常頻繁地清理微小的表).默認的比例因子適用於中小型表,但對於非常大的表則沒有那么多(在10GB表上,這大約是2GB的死元組,而在1TB表上則是~200GB).
這是一個累積大量死元組並立即處理所有這些元素的例子,這將會嚴重影響數據庫性能.根據前面提到的規則,解決方案是通過降低比例因子:
autovacuum_vacuum_scale_factor = 0.01
這將限制表中的數據變化達到1%時觸發autovacuum.
另一種解決方案是完全放棄比例因子,並僅使用閾值(建議使用)
autovacuum_vacuum_scale_factor = 0 autovacuum_vacuum_threshold = 10000
這將生成10000個死元組后觸發清理.
帶來的問題是在postgresql.conf中更改這些參數會影響所有表(或整個集群),並且不利於影響小表的清理(例如系統表).
當清理小表時,最簡單的解決方案是完全忽略問題,即使忽略問題,整體效果仍然非常明顯.
3.1 比較理想的解決方案
比較理想的解決方案是建議在postgresql.conf中忽略比例因子,設置較大的閾值(例如設置autovacuum_vacuum_scale_factor = 0和autovacuum_vacuum_threshold = 10000),然后根據各個表的delete和update頻繁程度以及表的數據量單獨為每個表設置閾值:
ALTER TABLE t SET (autovacuum_vacuum_scale_factor = 0); ALTER TABLE t SET (autovacuum_vacuum_threshold = 100)
詳細操作參看高級貨-設置pg_pathman創建的子表存儲參數(Parameter)
4 autovacuum工作進程數
autovacuum工作進程數配置選項是autovacuum_max_workers,清理不會發生在單個autovacuum進程中,但允許數據庫啟動autovacuum_max_workers個進程清理不同的數據庫/表.
問題是用戶認為autovacuum工作進程數與可能發生的清理量成正比.如果你將autovacuum工作進程數增加到6個,那么肯定會比默認的3個工人多做兩倍的工作,對吧?
不,幾段前描述的成本限制是全局性的,由所有autovacuum工作進程共享,每個工作進程只獲得總成本限制的1/autovacuum_max_workers,因此autovacuum工作進程數只會使它變得更慢.
這有點像高速公路,將汽車數量增加一倍但讓它們降低一半的速度,這只能讓你每小時到達目的地的人數相同.
因此,如果數據庫上的清理無法跟上用戶活動,那么增加工作者數量不是解決方案,除非您還調整其他參數。
5 小結
根據你的硬件和數據量修改postgresql.conf
autovacuum_vacuum_scale_factor = 0 #禁用比例因子 autovacuum_vacuum_threshold = 10000 #設置較大的閾值,然后根據各個表的delete和update頻繁程度以及表的數據量單獨為每個表設置閾值 autovacuum_vacuum_cost_limit = 1000 #根據硬件的配置(主要是磁盤IO)配置成本限制
因為在postgresql.conf中已經禁用autovacuum_vacuum_scale_factor,在創建表或修改表時根據業務需求單獨設置每張表的存儲參數.
修改已經存在的表
--根據實際情況決定是否設置
ALTER TABLE t set with (fillfactor=80, autovacuum_enabled=true,toast.autovacuum_enabled=true, autovacuum_vacuum_threshold=100,autovacuum_analyze_threshold=200, toast.autovacuum_vacuum_threshold=100);
注意如果是用ALTER TABLE設置的存儲參數,設置好后並不會生效,需要重新vacuum full表后才會生效.
新建的表
create table test( objectid bigserial not null, --唯一編號 ) with (
fillfactor=80, autovacuum_enabled=true,toast.autovacuum_enabled=true, autovacuum_vacuum_threshold=100,autovacuum_analyze_threshold=200, toast.autovacuum_vacuum_threshold=100
);
fillfactor 頁填充率,根據update和delete的發生程度設置.對於已經存在的表修改了這個參數是無效的,必須對重新vacuum full表后才會生效
autovacuum_vacuum_threshold 當update和delete更新了多少行數據開始autovacuum vacuum.注意如果沒有發生update和delete vacuum會自動跳過.
autovacuum_analyze_threshold 當update和delete更新了多少行數據開始autovacuum analyze.注意因為analyze無論是否發生update和delete都會從頭開始執行,因此需要估算好.
更改系統參數
ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.03; ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.03; ALTER SYSTEM SET autovacuum_vacuum_threshold = 300; ALTER SYSTEM SET autovacuum_analyze_threshold = 300;
建議:
1、autovacuum_max_workers的建議值為CPU核數/3。CPU資源充足,I/O性能較好時,可以適當加大。
2、對於更新頻繁的交易系統,如果系統資源充足,可以縮小autovacuum_vacuum_scale_factor 與 autovacuum_vacuum_threshold,讓vacuum清理頻繁
參考: https://blog.csdn.net/kmblack1/article/details/84953517