PostgreSQL 學習篇— PG 表膨脹
PG表膨脹原因
PG特殊的MVCC機制,delete不會真的刪除元組,update相當于delete+insert,舊元組本身不能通過DML語句來刪除,這樣就只有“漲”空間沒有“清理”空間,這就是表膨脹。
此時一般需要vacuum來清理死元組,把空間標記為可用,下次寫入時可以用到這部分空間;或者vacuum full等方式重寫表,讓表變得更加緊湊。
場景復現
OS:CentOS 7.6
db版本:PG 14.2
表名: cmdb_objects
圖片
表結構:
圖片
我先插入兩條數據,然后進行循環插入,只有object_type字段數據不同,用于后續做更新操作。
insert into cmdb_objects values ('xgrdb','xgr','test67','index');
insert into cmdb_objects values ('xgrdb','xgr','test67','table');
insert into cmdb_objects select * from cmdb_objects;
現在該表中存在32768條數據,再進行update更新操作:
update cmdb_objects set object_type='view' where object_type='table';
圖片
更新后該表會存在16384死行,如業務數據量大,且有頻繁的更新操作,該表就會產生高水位。可通過pg_stat_all_tables視圖來查詢該表中存在的死行、活躍行數。
select relname,n_live_tup,n_dead_tup from pg_stat_all_tables where relname='cmdb_objects';
圖片
處理方式
第一種處理方法vacuum:
把表中的dead tuples進行標記刪除,并沒有真正的物理刪除,表的實際使用空間沒有減小,只是將dead tuples的航空件轉換為可以使用的狀態,vacuum過程中,可以正常訪問表數據,不鎖表。
你也可以在執行碎片清理時收集統計信息:
vacuum(verbose,analyze) test;
16384死行已被刪除(該方式適用于實時更新的,更新數據量不大的表,可以每天進行批量操作)。
第二種處理方法vacuum full:
物理刪除表中的dead tuples,釋放空間給操作系統;vacuum full過程中,表被鎖定,不允許訪問。
圖片
執行前表大小如下:
圖片
執行vacuum full:
圖片
執行后表大小:
可以看到該表的使用空間已被物理刪除釋放。(該方式通過重建表,回收所有碎片空間,適用于經常進行大批量更新數據的表,定制策略進行執行,也可以在維護時間業務表每周執行一次。)
總結一下vacuum 與 vacuum full的區別:
- Vacuum不會阻塞查詢和DML業務;不會立即回收空間,只是把空間標記為可用,降低高水位;如果表的最后一個page沒有元組了,這個page會被truncate。
- Vacuum ful8級鎖,阻塞一切;表完全重寫,操作系統上對應的文件會被清理和重建;重建索引、FSM(可用空間文件)、VM (page可見性文件);會創建一個副本表,該表使用的磁盤空間最大可能翻至2倍,磁盤空間不足,謹慎執行。
第三種處理方法autovacuum參數控制:
圖片
autovacuum是postgres里面一個服務端進程,可在一定條件下自動觸發執行。
該參數值默認為 on(開啟全局自動),主要作用包括:
- 清理死元組(UPDATE或DELETE操作后留下的),并對表進行分析;
- 更新可用空間映射(free space map),以跟蹤表塊中的可用空間;
- 更新僅索引掃描所需的可見性圖(visibility map);
- 凍結(freeze)表行,以便事務ID計數器可以安全地環繞。
表膨脹的危害:
- 表占用過大的空間;
- 進而引起sql性能降低;
- 表過大會也會導致vacuum清理時間變成長;vacuum full阻塞時間也會變長,不過可以通過pg_repack來代替vacuum full,減少阻塞時間。
以上操作都會增加cpu與io的資源消耗。
補充:有一種表膨脹情況比較難處理,autovacuum自動清理死元組速度趕不上生成速度。因為update、 insert、delete事務并發太多,這次的vacuum還沒來得及清理出之前的可用空間,就有大量update生成新的空間和死元組,導致表不斷膨脹。