空间回收

更新时间:

当对数据库执行UPDATEDELETE操作时,虽然数据表面上已被删除,但实际上只是被标记为不可见,并留有“空洞”在数据页中。这会导致在读取数据时,这些“空洞”也会一同被加载,从而减慢了数据的扫描速度。因此,定期清理这些已删除的空间可提高数据读取效率。

  • 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因长时间持有排他锁而阻塞该表的读写操作,影响在线业务。

注意事项

  • 该方法在空间回收过程中会生成一份副本数据,短时间内会导致磁盘使用量上升。

  • 该方法在后台会重建索引,期间同样会阻塞读写操作。因此不适用于包含较大索引的表(例如向量索引)。

操作步骤

  1. 在目标库内,创建以下函数。

    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;
    
  2. 调用函数回收脏数据。

    SELECT reorganize_table('schema_name.table_name');