為什麼 vacuum 後表還是繼續膨脹?

此賬號為華為雲開發者社群官方運營賬號,提供全面深入的雲計算前景分析、豐富的技術乾貨、程式樣例,分享華為雲前沿資訊動態

本文分享自華為雲社群《為什麼vacuum後表還是繼續膨脹?》,原文作者:大威天龍:-。

vacuum 簡介 :

對於 GaussDB 中的行存表,在更新元組或者刪除元組後,舊版本的資料仍然存在,僅在元組頭資訊中標記了刪除或更新的事務號(xmax)。對於更新和刪除操作頻繁的表,會存在大量垃圾資料,導致磁碟空間的浪費和查詢掃描時額外的 IO 開銷,需要定期執行清理操作(vacuum)來控制行存表以及表上索引的膨脹。

vacuum 操作的內部原理:

Vacuum 的主要步驟:

1。 移除死亡元組並對滿足條件的老元組執行 frozen 操作。

2。 移除指向死亡元組的索引元組,更新對應表的 fsm 和 vm 檔案

FSM: free space map 空閒空間對映檔案,插入資料時會根據該檔案來選擇合適的 page。

VM: visibility map 可見性對映檔案,後續 vacuum 時會根據該檔案來選擇是否掃描某個 page,提高 vacuum 效率;同時在進行 index-only-scan 時也會使用該檔案來提高可見性判斷的效率)。

3。 更新統計資料 pg_stat_all_tables。 Linepointer 不會被移除,用於在之後複用。

示意圖如下:

vacuum 之前

為什麼 vacuum 後表還是繼續膨脹?

vacuum 之後

為什麼 vacuum 後表還是繼續膨脹?

為什麼 vacuum 後表還是繼續膨脹

影響 vacuum 效果的因素

1。 Oldestxmin 的推進

vacuum 只能清理掉當前全域性

存活的最老事務

(OldestXmin)之前的事務所產生的垃圾資料,所以如果仍然存在老事務的話(比如長事務或者長 sql 的存在),新事務所產生的垃圾資料並不會被 vacuum 立即清理。例如:

會話 1

:新建表 row_tbl 並插入一條資料,起一個事務先不提交

gaussdb=# create table row_tbl(a int, b int);CREATE TABLEgaussdb=# insert into row_tbl values(1,1);INSERT 0 1gaussdb=# begin;BEGINgaussdb=# SELECT txid_current_snapshot(); txid_current_snapshot————————————- 210115:210115:(1 row)gaussdb=# SELECT txid_current(); txid_current———————— 210115(1 row)

會話 2

:刪除資料後做 vacuum 清理操作,再次插入資料,資料也沒有複用之前空間;查詢檢視可以發現垃圾資料並沒有被清理掉。

gaussdb=# select ctid,* from row_tbl; ctid | a | b————-+——-+——- (0,1) | 1 | 1(1 row)gaussdb=# delete row_tbl;DELETE 1gaussdb=# SELECT txid_current_snapshot(); txid_current_snapshot————————————- 210115:210122:(1 row)gaussdb=# vacuum row_tbl;VACUUMgaussdb=# insert into row_tbl values(2,2);INSERT 0 1gaussdb=# select ctid,* from row_tbl; ctid | a | b————-+——-+——- (0,2) | 2 | 2(1 row)gaussdb=# select n_dead_tup, last_vacuum from pg_stat_all_tables where relname=‘row_tbl’; n_dead_tup | last_vacuum——————+————————————————- 1 | 2021-06-10 20:04:58。987631+08(1 row)

會話 1

:將會話 1 的事務結束再執行 vacuum,查詢檢視可以發現沒有死亡元組,插入資料可以發現複用了舊的空間(即 ctid 是(0,1)的空間)。

gaussdb=# SELECT txid_current_snapshot(); txid_current_snapshot————————————- 210136:210136:(1 row)gaussdb=# vacuum row_tbl;VACUUMgaussdb=# select n_dead_tup, last_vacuum from pg_stat_all_tables where relname=‘row_tbl’; n_dead_tup | last_vacuum——————+————————————————- 0 | 2021-06-10 20:09:10。516564+08(1 row)gaussdb=# insert into row_tbl values(3,3);INSERT 0 1gaussdb=# select ctid,* from row_tbl; ctid | a | b————-+——-+——- (0,1) | 3 | 3 (0,2) | 2 | 2(2 rows)

2。 LinePointer 狀態還未處於 unused

為什麼 vacuum 後表還是繼續膨脹?

元組被刪除後,只有當 vacuum 將元組的 LinePointer(或者叫 item pointer, 指向具體的元組)置為 LP_UNUSED 狀態後,該 LinePointer 才有可能在新插入資料時複用。

3。 Fsm 還未生成插入資料時,依賴 fsm 檔案來選擇可用的 page,如果 fsm 沒有生成則會導致使用新的 page 而不是複用舊的。

4。 批次匯入在舊版本 Gaussdb 中,對錶進行批次插入資料的操作時,會直接申請新的 page 來插入資料。所以在某些場景下雖然 vacuum 後清理了髒資料,但由於業務場景以批次插入為主,導致 vacuum 對膨脹的控制效果並不理想。目前已經支援批次插入資料時對空間的複用。

一些建議與總結

1。 儘量避免長事務,可以透過檢視 pg_running_xacts 檢視是否有老事務沒有結束或者兩階段事務殘留

2。 定期做 vacuum 來及時回收垃圾空間

3。 對於已經膨脹的索引可以透過 reindex 來縮小大小。

4。 vacuum 能清理垃圾資料,但無法將這些空間還給作業系統,對於已經膨脹的表只能透過 vacuum full 來縮小大小。

想了解 GuassDB(DWS)更多資訊,歡迎微信搜尋“GaussDB DWS”關注微信公眾號,和您分享最新最全的 PB 級數倉黑科技~

點選關注,第一時間瞭解華為雲新鮮技術~