PostgreSQL vacuum 核心原始碼機理

jesselyu發表於2015-05-02

 

    為了能全面理解PG整個vacuum的過程,專門繪製了下面一個完整的過程圖,用以記錄其核心原始碼實現機理。整個過程比較複雜。大致分為以下幾個部分:

一、pg_ctl

    這個負責PG server起動,呼叫start_postmaster後,起動PG主server程式postmaster。後面任務交給postmaster來處理。

二、postmaster

    負責起動autovacuum launcher,起動autovacuum launcher後臺程式後,控制權交給launcher。

三、autovacuum launcher和 autovacuum worker

     這兩個程式的原始碼都在autovacuum.c中,autovacuum.c中的原始碼可以大致上為分兩個階段:

     第一個階段:主要為launcher的職責。負責為將要起來工作的worker分配AutoVacuumShemStruct結構槽,並且選擇一個最近最少vacuum或者最需要凍結xid的DB。

         然後將此dbid賦值給worker,併傳送訊號給postmaster,說已經準備好woker了,可以起動了。

     第二個階段:主要為worker的職責。postmaster接收到launcher的訊號後,起動AutoVacuumShemStruct中狀態為startingwork的auto vacuum worker。

        主要是工作是fork一個程式,並將pid賦值給此worker,然後發訊號給launcher,說你可以準備起下一個worker了。

 

進入autovacuum worker後:

1.首先收集需要vacuum或者analyze的表。需要vacumm的表的判斷依據是Dead Tuples的值超過:vacuum_threshold + vacuum_scale_factor * num_tuples。

需要analyze的表的判斷依據是DML的記錄超過此值:analyze_threshold + analyze_scale_factor*num_tuples。

 

2.找到第一個需要vacuum的表,並將此表的 vacuum option引數賦值給autovacuum worker,即MyWorkInfo結構體(指向AutoVacuumShemStruct中的worker)。

如cost_limit,cost_delay,cost_limit_base引數。其中cost_limit_base的值等於cost_limit的值。

 

3.調整每個在running 的worker的cost_limit值。worker中有cost_limit和cost_limit_base兩個變數。cost_limit和cost_limit_base的值,在剛開始沒有經過balance時,是相等的。

但是經過balance過,cost_limit的值會發生變化。而cost_limit_base的值基本上就是表級別用”alter table xxx set(autovacuum_vacuum_cost_limt=1000)”命令來設定的值。

設定好後,在balance過程中,cost_limit_base的值基本上不變。因此才在名字上加了”base”用於區別。如果沒有設定,cost_limit和cost_limit_base都是autovacuum_vacuum_cost_limit這個在postgresql.conf配置的值。
表級別的設定值優先順序高於postgresql.conf中定義的全域性值。balance時,參考cost_limit_base的值對每個running worker 的cost_limit進行調整。cost_limit最大值

不超過cost_limit_base,即需在[1,cost_limit_base]閉區間內。cost_limit值的意義其實就是每個毫秒時間內vacuum所能允許消耗的IO cost值。即:cost_limit/cost_delay。

而這個cost_limit/cost_delay又會被autovacuum_max_workers個workers平分掉,而不是簡單的做個限制。如果超過,就是最高以4倍的cost_delay來懲罰,這將導致vacuum過程嚴重變長。

詳細說明見”第6“步。

 

4.balance完成後,接著就用上面的cost_limit和cost_delay值去更新VacuumCostDelay和VacuumCostLimit這兩個值。並將VacuumCostBalance的值置為“0”。這三個值的意義在”第6“步中講。

 

5.接著去判斷此次發起的是哪種型別的vacuum: vacuum full 還是lazy vacuum。如果是vacuum full,相當於重建relation,需要AccessExclusiveLock。如果是lazy vacuum,

則是SharedUpdateExclusiveLock。lazy vacuum 還要判斷是否需要full scan。如果是full scan,需要更新pg_class中的relfrozenxid值。partial scan只清理dead tuple和compact free space。
請見另外一篇文章“PostgreSQL vacuum原理—vacuum揭秘”。

 

6.最後就是去scan heap了。按每個block依次遍歷。每遍歷一個block都需要 delay一次。在這裡上面“第4步”中的三個值就派上用場了。每次sleep的時間按公式:

msec=VacuumCostDelay*VacuumCostBalance/VacuumCostLimit

如果上面這個值大於4倍的VacuumCostDelay值,那麼msec就等於“4*VacuumCostDelay”。否則就按上面公式算出的值進行sleep。sleep完成後,將MyWorkInfo中的cost_limit和cost_delay

賦值給VacuumCostDelay,VacuumCostLimit,以適應balance過程。然後再次將VacuumCostBalance設定為“0”。

其中VacuumCostDelay和VacuumCostLimit基本上每次balance後就不變了。唯有VacuumCostBalance值是在整個block遍歷的過程中變化的,它的cost計算是根據當前page hit,page miss

以及page dirty來計算的,請見另外一篇文章“PostgreSQL Cost Based Vacuum探秘”。因此如果一個表的update,delete操作很高,那麼dead tuple就非常多,導致vacumm時,

VacuumCostBalance值變高。因此如果要加快vacuum,減少一個表在vacuum時的delay時間,只有把cost_limit的值提高,也就是postgresql.conf中autovacuum_vacuum_cost中的值調大,

才會使上面公式的值就小。

 

上面還是太理論了,還是有點難懂的。我舉個簡單的例子來說明下cost的計算:

假設當前有3個表需要vacuum,而且起了3個worker。每個表的autovacuum_vacuum_cost_limit都為預設值200,autovacuum_vacuum_cost_delay的值都為10ms。另外postgresql.conf中

配置的這兩個值也是預設值,分別為200和10ms。那麼cost_limit 的值就是:200/3=66。當然,如果在每個表的cost_limit_base都不同的情況下,計算會複雜點。總體思路就是cost_limit_base

大的表,分到的cost_limit值也大些。但是區間還是在”第3步”中的區間,即[1,cost_limit_base]。cost_limit的可配置範圍在[1,10000];cost_delay的值在[1,100],單位是ms。

計算公式我歸納如下:

cost_total=sum(cost_limit_base/cost_limit_delay),每個runing worker都加起來。

cost_avail=autovacuum_vacuum_cost_limit/autovacuum_vacuum_cost_delay,postgresql.conf中配置的值。

每個worker的cost_limit=max(min(cost_avail*cost_limit_base/cost_total,cost_limit_base),1)。所以上面的值代入,得到約為66。

 

PG用這種演算法,完善實現了cost based vacuum。詳細圖見下:

vacuum

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30088583/viewspace-1618496/,如需轉載,請註明出處,否則將追究法律責任。

相關文章