空间回收
当对数据库执行UPDATE
或DELETE
操作时,虽然数据表面上已被删除,但实际上只是被标记为不可见,并留有“空洞”在数据页中。这会导致在读取数据时,这些“空洞”也会一同被加载,从而减慢了数据的扫描速度。因此,定期清理这些已删除的空间可提高数据读取效率。
AnalyticDB for PostgreSQL支持在后台对表进行脏数据自动回收(Auto Vacuum)操作。
为了在大量的删除或更新操作后及时清理表,您也可以针对整个数据库或单个表手动执行
VACUUM
操作。
空间回收方法
使用VACUUM
命令,可以对表进行重新整理,回收空间,以便获取更好的数据读取性能。VACUUM命令的语法如下:
VACUUM [FULL] [FREEZE] [VERBOSE] [table];
VACUUM
:不带任何参数时,会对所有表执行VACUUM操作。VACUUM [table]
:对单表执行VACUUM,清理脏数据,释放列存表的脏数据空间和行存表尾部的“空洞”,不阻塞读写。VACUUM FULL [table]
:对单表执行VACUUM FULL,会彻底释放表的脏数据空间,但是会申请排他锁阻塞读写。VACUUM FREEZE [table]
:对单表执行VACUUM FREEZE,降低单表的XID AGE,不阻塞读写。VACUUM VERBOSE [table]
:会打印VACUUM执行过程中各节点回收脏数据行数的日志。
注意事项
AnalyticDB for PostgreSQL实例默认开启Auto Vacuum功能,后台将自动对表进行脏数据回收操作,同时降低XID AGE。
在执行大量删除或更新操作后,您可以手动执行
VACUUM FULL
命令,以彻底地释放表占用空间。重要VACUUM FULL
会申请排他锁,阻塞对该表的读写操作。只有初始账号或高权限用户RDS_SUPERUSER才能执行
VACUUM
操作。如果在没有相应权限的情况下执行VACUUM
,该操作不会产生任何效果。
查询需要执行VACUUM的表
AnalyticDB for PostgreSQL提供了智能诊断数据膨胀功能,您可以利用该能力找到膨胀的表,按照建议查询需要执行VACUUM
的表。具体内容,请参见。
AnalyticDB for PostgreSQL提供了gp_bloat_diag视图,该视图会统计当前页数和实际需要页数的比例。您也可以通过ANALYZE TABLE
来收集统计信息之后,查看该视图。
SELECT * FROM gp_toolkit.gp_bloat_diag;
以下结果包含发生了中度或者显著膨胀的表。对于这些表,可以执行VACUUM FULL
来回收空间。
bdirelid | bdinspname | bdirelname | bdirelpages | bdiexppages | bdidiag
----------+------------+------------+-------------+-------------+---------------------------------------
21488 | public | t1 | 97 | 1 | significant amount of bloat suspected
(1 row)
当实际页面数与预期页面数的比率(bdirelpages / bdiexppages
)超过4但小于10时,会被判定为中度膨胀;而当该比率超过 10 时,则会被判定为显著膨胀。
空间回收期间可读
在执行大量删除或更新操作后,您可以通过手动执行VACUUM FULL
命令彻底释放表所占用的存储空间。然而,VACUUM FULL
会申请排他锁,阻塞对该表的读写操作,影响在线业务。针对这种情况,下文介绍一种在空间回收期间确保表可读的清理方案。
基本原理
该方法的本质是通过对表进行数据重分布来实现空间回收。其原理是:在后台创建一个与原表结构相同的临时表,将原表的数据全量写入临时表,然后交换两表的元数据,并删除临时表。由于数据被完全重写,新生成的数据文件中不存在“空洞”,从而实现了存储空间的有效回收。
适用场景
自动回收空间(Auto Vacuum)由于锁退让机制,可能无法及时回收表空间,因此需要主动维护。
对于数据膨胀严重的大表,执行
VACUUM FULL
因长时间持有排他锁而阻塞该表的读写操作,影响在线业务。
注意事项
该方法在空间回收过程中会生成一份副本数据,短时间内会导致磁盘使用量上升。
该方法在后台会重建索引,期间同样会阻塞读写操作。因此不适用于包含较大索引的表(例如向量索引)。
操作步骤
在目标库内,创建以下函数。
CREATE OR REPLACE FUNCTION reorganize_table(table_name regclass) RETURNS void AS $$ DECLARE distribution_policy text; alter_sql text; BEGIN SELECT pg_get_table_distributedby(table_name) INTO distribution_policy; alter_sql := format('ALTER TABLE ONLY %s SET WITH (REORGANIZE=true) %s READABLE', table_name, distribution_policy); EXECUTE alter_sql; END; $$ LANGUAGE plpgsql;
调用函数回收脏数据。
SELECT reorganize_table('schema_name.table_name');