1 問題
最近發現一個奇怪的問題,再使用pg_dump備份一個庫時候,發現備份后的大小只有幾個G大小,但是統計了整個數據庫的大小,發現居然超過了1000G。剛開始還以為備份過程中中斷出現問題了,重新備份一次大小還是一樣。后來發現是出現了膨脹非常厲害的表導致的。
2 解決
2.1 膨脹表的產生
首先需要從pg的MVCC機制說起,MVCC即多版本並發控制,也就是說,在PostgreSQL中,一次行的update和delete不會立即刪除舊的版本,而是被標記為刪除,一旦被標記刪除的行數非常多后,統計信息發現表中預期的行數和實際的行數相差很大,表的膨脹程度就很大。但是最后,任何事務都不會再對一個過時或者被刪除的行版本感興趣,而被標記為刪除的行所占的空間必須被收回來用於新行,這樣可以避免磁盤空間需求無限制增長,這通過運行VACUUM命令來完成。
2.2 膨脹表的維護
vacuum的標准形式移除表和索引中的死亡行版本並將空間標記為未來可以重用,不過,它並不會將該空間交給操作系統,除非在一些特殊的情況下,表的尾部的一個或多個頁面變成完全空閑並且能夠很容易地得到一個排他表鎖。
vacuum full通過把死亡空間之外的內容寫成一個完整的新版本表文件來主動緊縮表,這將最小化表的尺寸,但是同時花費的時間也是巨大的。它也需要額外的磁盤空間來用於表的新副本創建,直到操作完成,完成期間,這張表會加上排它鎖,任何事務都會被block掉。
這兩種命令到底什么時候用呢?這里其實建議例行清理的一般目標是多做標准的vacuum來避免需要vacuum full。自動清理守護進程和能嘗試這樣的工作,但是永遠也不會進行vacuum full。在這種情況下,其思想是不讓表保持他們的最小尺寸,而是保持磁盤空間使用的穩定狀態:每個表占用的空間等於其最小尺寸加上清理被標記為刪除的空間。如果該表將來預期還會再次增長這樣就沒有意義,因此,對於維護頻繁被更新的表,周期性運行標准的vacuum比少量運行vacuum full更好。
2.3 查看膨脹表
greenplum中每一個庫下面都有一個gp_toolkit模式,里面有一個gp_bloat_diag的視圖,其查詢的是系統表中該庫中膨脹表信息。
各個字段的意義如下:
Table 1. gp_bloat_diag view
Column | Description |
---|---|
bdirelid | Table object id. |
bdinspname | Schema name. |
bdirelname | Table name. |
bdirelpages | Actual number of pages on disk. |
bdiexppages | Expected number of pages given the table data. |
bdidiag Bloat | diagnostic message.diagnosis of bloat (ratio from 1 to 3 -> no bloat, ratio from 4 to 10 -> moderate bloat, ratio > 10 -> significant bloat) |
查看有哪些表膨脹比較厲害,建議使用排序的方式,這樣可以查看到哪些表的relpage比較大,而expect page比較少的情況。
select * from gp_toolkit.gp_bloat_diag order by bdirelpages desc, bdidiag
上述命令執行后,得到了如下的結果
bdirelid | bdinspname | bdirelname | bdirelpages | bdiexppages | bdidiag Bloat |
---|---|---|---|---|---|
1249 | pg_catalog | pg_attribute | 1599965 | 8 | significant amount of bloat suspected |
67535346 | ad | ad_big_table | 399419 | 10246 | significant amount of bloat suspected |
1259 | pg_catalog | pg_class | 123130 | 8 | significant amount of bloat suspected |
5094 | pg_catalog | gp_relation_node | 77010 | 8 | significant amount of bloat suspected |
1247 | pg_catalog | pg_type | 46211 | 8 | significant amount of bloat suspected |
2608 | pg_catalog | pg_depend | 20316 | 22 | significant amount of bloat suspected |
可以很明顯看到,pg_catalog下的幾張表膨脹非常厲害,都屬於嚴重膨脹表了,可能vacuum都不能解決,需要立馬運行vacuum full命令。
統計表的信息時候也提示如下:
ANALYZE pg_catalog.pg_attribute
NOTICE: ANALYZE detected all empty sample pages for table pg_attribute, please run VACUUM FULL for accurate estimation.
下面查看一下排在第一的那張表pg_attribute大小。
select pg_size_pretty(pg_relation_size('pg_attribute'));
pg_size_pretty
--------------
440 GB
那么問題找到了,就是系統表占用了大量的庫空間,pg_attribute存儲的是有關表列的信息,數據庫中每一個表的每一個列都恰好在pg_attribute中有一行,這其中也會有索引的屬性項,並且事實上,所有具有pg_class項的對象在這里都有屬性項。
2.4 解決辦法
找一個業務不太繁忙的時間,對上述膨脹非常厲害的表進行vacuum full操作,收縮表空間實際占用的大小。同時開始每天定時查詢膨脹表進行vacuum操作,維持表占用空間的相對穩定。
補充:經過將近14個小時的表壓縮,目前該表已經正常,從falcon監控可以看到,每一台虛擬機減少的空間大小都大概在200G,說明該表已經成功被縮減空間了,后期需要腳本定期執行,維持表占用空間的穩定。
select pg_size_pretty(pg_relation_size('pg_attribute'));
pg_size_pretty
--------------
34 MB
該腳本可以參考 https://github.com/YICHAO1/scripts/blob/master/bash/vacuumgp .sh