天天看點

Postgresql為什麼需要Freeze?

Freeze

PG需要為每個事務配置設定事務ID,這是MVCC能工作的基礎,類似于一個時間戳的概念。

時間戳的做基本動作就是比較大小,即需要得到哪條事務在先,哪條在後。

首先我們來看一下PG中事務ID的比較邏輯,這部分在之前的某一起DB月報中也介紹過。

/*
 * TransactionIdPrecedes --- is id1 logically < id2?
 */
bool
TransactionIdPrecedes(TransactionId id1, TransactionId id2)
{
  /*
   * If either ID is a permanent XID then we can just do unsigned
   * comparison.  If both are normal, do a modulo-2^32 comparison.
   */
  int32   diff;
​
  if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
    return (id1 < id2);
​
  diff = (int32) (id1 - id2);
  return (diff < 0);
}      

TransactionIdIsNormal

宏的作用是判斷id1是否大于等于3。這裡值得注意的是

diff = (int32) (id1 - id2)

。這裡使用了一個程式設計技巧,比如發生了事務ID回卷:

id1 = 4294967200
id2 = 100
​
// int32    -2147483648 ~ 2147483647 (32億+)
// uint32   0 ~ 4294967295 (42億+)      

id1 - id2 = 4294967100

這個值強轉成(int32)類型後是一個負數,會發生

return true

,函數會認為

id1 < id2

,也就是說id2是更新的事務。

我們繼續考慮下一個場景,id2繼續增長到21億多時,id1和id2的內插補點已經在int32的範圍内了,是以diff會大于零,

return false

函數會認為

id1= 42億+ > id2= 21億+

,結論是id1是更新的事務!這裡就出現問題了,一個老事務被判斷成了新事務。

是以PostgreSQL必須保證一個資料庫中兩個有效的事務之間的年齡差最多是,約為20億。

1 Lazy Freeze

關注參數:

vacuum_freeze_min_age = 50000000(5千萬)      

lazy freeze是autovacuum和vacuum都會做的動作,是以vm在這裡也是有效的,隻有vm中标記的頁面會做lazy freeze。

觸發條件:

xmin小與freeze_txid的元組都會被freeze。

freeze_txid = current_oldest_xmin - vacuum_freeze_min_age
            = 目前活躍的最小的xid - 參數vacuum_freeze_min_age      

例如目前活躍的最小xid=50005100,vacuum_freeze_min_age為預設值50000000,那麼freeze_txid=5100,也就是說xmin小于5100的元組都會被freeze(前提是所在的頁面會被掃描到) 

Postgresql為什麼需要Freeze?

這裡補充一個長事務的例子:

資料庫運作一個長事務,很久沒有送出導緻current_oldest_xmin一直不會超過vacuum_freeze_min_age,vacuum不會當機任何元組。這樣最低的xmin就和目前最新的xmin的距離越來越遠,內插補點慢慢接近20億,這時候資料庫為保證資料不丢失,會有告警甚至當機。

告警

WARNING:  database "mydb" must be vacuumed within 177009986 transactions
HINT:  To avoid a database shutdown, execute a database-wide VACUUM in "mydb".      

當機

ERROR:  database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT:  Stop the postmaster and vacuum that database in single-user mode.      

2 Eager Freeze

vacuum_freeze_table_age = 150000000(1億5千萬)      
pg_database.datfrozenxid < (OldestXmin−vacuum_freeze_table_age)
​
pg_database.datfrozenxid : 目前資料庫被當機的最大的事務ID,pg_database中可以查到      

例如我們查詢到目前的pg_database.datfrozenxid和vacuum_freeze_table_age的值為

select oid,datname,datfrozenxid from pg_database where oid=13214;
  oid  | datname  | datfrozenxid
-------+----------+--------------
 13214 | postgres |          548

show vacuum_freeze_table_age;
 vacuum_freeze_table_age
-------------------------
 150000000      

這種情況下當

OldestXmin > 150000000 = 548

也就是目前最小的活躍事務大于1億4千萬+的時候才會觸發eager freeze。

是以觸發eager freeze的條件可以這樣了解,目前最小的活躍事務ID 與 資料庫最大的FREEZE事務ID的內插補點超過了1億5千萬(vacuum_freeze_table_age),就會觸發eager freeze。

Postgresql為什麼需要Freeze?