更新时间:2026-07-28 GMT+08:00
分享

VACUUM

GaussDB中,当数据被更新或删除时,系统并不会立即释放物理空间,而是将其标记为“可重用”。随着时间推移,表中的死元组会越来越多。死元组(dead tuples)是指已被删除或因更新而被新版本覆盖,不再被任何活跃事务引用的历史数据行。

上述情况不仅会导致存储空间利用率下降,也可能影响查询性能,因为查询需要遍历这些无用的元组。而VACUUM命令的主要任务就是清理这些死元组,并回收相应的存储空间。

为什么需要VACUUM

概括来说,VACUUM的核心目的就是清理持续累积的死元组、回收其占用的可重用空间,从而抑制Astore表膨胀、保障空间利用率与查询性能。

Astore存储引擎采用多版本并发控制(MVCC)来保证数据在高并发环境中的一致性和可访问性,每个事务在它开始的时间点都在其自己的数据库快照上运行,这就意味着无法立即删除过时的数据。当进行UPDATE和DELETE操作时,Astore存储引擎会为其他正在运行中的事务保留数据的历史版本:对于删除操作,并不会真正在数据页上删除元组,而是标记其被删除,这些被标记删除的元组即死元组;更新操作同样采用标记机制,相当于先标记删除原数据行,再插入新数据行。由于删除并非真正删除数据,死元组依旧占用磁盘空间,就会出现虽然查询表数据很少,但是表所占用磁盘空间很大的情况。此外,由于Astore的更新是基于追加写的,数据的新老版本在数据页面上共存,这进一步导致了数据库表空间的膨胀。

需要说明的是,Ustore存储引擎的日常旧版本回收由Undo子系统自治完成,不依赖VACUUM;但Ustore仍支持VACUUM与VACUUM FULL(如彻底回收物理空间),且系统表清理与空闲空间映射(FSM)维护仍依赖autovacuum。

VACUUM的优势是什么

  • 控制表膨胀:清理死元组并维护FSM,将过时数据占用的空间标记为可重用,供后续插入操作使用,避免表空间持续膨胀,提升空间复用能力。
  • 轻量非阻塞:普通VACUUM在清理过程中获取较低级别的锁,不阻塞业务的正常读写,对数据库性能影响较小,可在业务运行期间执行。
  • 冻结旧事务:在清理过程中尝试冻结旧事务,使元组对所有快照可见,从而在可见性判断、修改前检查等环节起到加速作用,便于推进表的冻结事务号(relfrozenxid)并回收CLOG/CSNLOG等事务文件。
  • 支持自动清理:通过autovacuum守护线程,数据库可在死元组达到阈值时自动触发清理与统计信息更新,减少人工运维负担。

VACUUM的使用场景

  • 高频增删改表的膨胀治理:对于订单、账务等存在大量UPDATE/DELETE的Astore业务表,运维团队常面临表占用空间远大于有效数据的膨胀问题。此时定期对表执行VACUUM,可清理死元组并将空间标记为可重用,从而控制表膨胀、维持稳定的查询性能,且执行期间不阻塞正常业务读写。
  • 彻底回收磁盘空间:当业务表已严重膨胀、需要将磁盘空间真正归还操作系统时,运维人员可在业务低谷期对表执行VACUUM FULL。该操作会重建原表并物理释放空间,但执行期间会锁表,期间无法对该表进行读写访问,因此适合在停业务窗口或低峰期对大表进行整理。
  • 常态化自动维护:在无需人工干预的常规运维中,DBA可开启autovacuum,由守护线程自动监测各表的死元组数量,在达到阈值时自动执行VACUUM与统计信息收集,使表膨胀和统计信息陈旧问题在日常运行中被持续抑制。

VACUUM的工作原理

VACUUM的执行可概括为“扫描清理、空间维护、事务冻结、信息更新”几个环节。系统首先扫描表中的页面,默认Lazy模式下会借助可见性映射(VM)跳过全可见页面,并将其中的死元组标记为可重用状态,但普通VACUUM并不重新组织活跃元组的物理存储位置,也不立即把空间归还操作系统,而是通过维护FSM记录可用空间,供后续插入复用。

在此基础上,VACUUM会尝试冻结旧事务,将元组事务标记为对所有快照可见,以加速可见性判断;同时更新VM、统计信息并回收事务文件等。普通VACUUM清理时获取较低级别的锁,不阻塞业务的正常读写。

自动清理由autovacuum守护线程实现,它由launcher与worker两类线程协同:autovacuum launcher在参数开启时常驻,负责调度;autovacuum worker实际连接数据库并对选定的表执行VACUUM。其触发依据死元组数量阈值,当死元组数超过autovacuum_vacuum_scale_factor × 表行数 + autovacuum_vacuum_threshold时即触发自动清理。通过手动VACUUM与autovacuum的配合,数据库得以在长期运行中持续回收空间、维护统计信息,保障性能稳定。

VACUUM的分类

按照清理力度和触发方式,VACUUM相关操作可分为以下几类:
  1. 按清理力度,可分为普通VACUUM与VACUUM FULL。普通VACUUM仅标记并复用死元组空间、轻量非阻塞;VACUUM FULL重建表并将空间归还操作系统、需锁表,清理更彻底但代价更高。
    • VACUUM
      • 示例
      --创建表tbl_test,并插入数据。
      gaussdb=# CREATE TABLE tbl_test(c1 int); 
      gaussdb=# INSERT INTO tbl_test VALUES (1); 
      
      --查看数据,和数据的ctid。
      gaussdb=# SELECT ctid,* FROM tbl_test;
       ctid  | c1 
      -------+----
       (0,1) |  1
      (1 row)
      
      --删除该数据。
      gaussdb=# DELETE FROM tbl_test;
      
      --重新插入一条数据,使用了一个新的ctid。
      gaussdb=# INSERT INTO tbl_test VALUES (2);
      gaussdb=# SELECT ctid,* FROM tbl_test;
       ctid  | c1 
      -------+----
       (0,2) |  2
      (1 row)
      
      --使用VACUUM命令回收之后,在插入数据,复用了旧的空间。
      gaussdb=# VACUUM ANALYZE tbl_test;
      gaussdb=# INSERT INTO tbl_test VALUES (3);
      gaussdb=# SELECT ctid,* FROM tbl_test;
       ctid  | c1 
      -------+----
       (0,1) |  3
       (0,2) |  2
      (2 rows)
      
      --删除表。
      gaussdb=# DROP TABLE tbl_test;
    • VACUUM FULL
      • 示例
      --建表。
      gaussdb=# CREATE TABLE tbl_test2(c1 int);
      
      --插入10万条数据并查看表的大小。
      gaussdb=# INSERT INTO tbl_test2 VALUES (generate_series(1,100000));
      gaussdb=# SELECT 'tbl_test2' AS tablename, pg_size_pretty(pg_relation_size('tbl_test2')) AS size;
       tablename |  size   
      -----------+---------
       tbl_test2 | 3048 kB
      (1 row)
      
      --删除数据并查看表大小。
      gaussdb=# DELETE FROM tbl_test2;
      gaussdb=# SELECT 'tbl_test2' AS tablename, pg_size_pretty(pg_relation_size('tbl_test2')) AS size;
       tablename |  size   
      -----------+---------
       tbl_test2 | 3048 kB
      (1 row)
      
      --使用VACUUM FULL回收空间,并查看表的大小。
      gaussdb=# VACUUM FULL ANALYZE tbl_test2;
      gaussdb=# SELECT 'tbl_test2' AS tablename, pg_size_pretty(pg_relation_size('tbl_test2')) AS size;
       tablename |  size   
      -----------+---------
       tbl_test2 | 0 bytes
      (1 row)
  2. 按触发方式,可分为上文已介绍的手动VACUUM与自动清理(autovacuum)。手动VACUUM由用户主动执行,可控性强;autovacuum由守护线程根据死元组阈值自动触发,适合常态化维护。需要注意,业务DDL可终止autovacuum进程,但无法终止手动执行的VACUUM。
    • 自动清理(autovacuum)
      表1 autovacuum参数说明

      参数名称

      默认值

      参数解释

      autovacuum

      on

      控制autovacuum是否打开。

      autovacuum_naption

      600

      控制两次autovacuum操作的时间间隔。默认值为600,表示600秒。

      autovacuum_mode

      mix

      控制autovacuum线程的具体操作:

      • analyze:表示只执行autoanalyze。
      • vacuum:表示只执行autovacuum。
      • mix:表示autoanalyze和autovacuum都执行。
      • none:表示autoanalyze和autovacuum都不执行。

      autovacuum_max_workers

      3

      同时运行autovacuum worker的最大线程数。

      autovacuum_vacuum_scale_factor

      0.2

      用于判断是否触发VACUUM。

      取值范围:0.0~100.0。

      autovacuum_vacuum_threshold

      50

      用于判断是否触发VACUUM。

      取值范围:0~2147483647。

      autovacuum_analyze_scale_factor

      0.1

      用于判断是否触发ANALYZE。

      取值范围:0.0~100

      autovacuum_analyze_threshold

      50

      用于判断是否触发ANALYZE。

      取值范围:0~2147483647

      autovacuum_vacuum_cost_limit

      -1,表示该参数同vacuum_cost_limit(控制手动vacuum的参数)。

      autovacuum操作使用的开销值,即开销达到该值之前不会进入休眠。

      autovacuum_vacuum_cost_delay

      -1,表示该参数同vacuum_cost_delay(控制手动vacuum的参数)。

      autovacuum操作开销达到autovacuum_vacuum_cost_limit值之后休眠的时间。

    当死元组数量达到阈值就会触发autovacuum,具体的阈值计算公式如下:
    number_of_dead_tuples > autovacuum_vacuum_scale_factor * reltuples + autovacuum_vacuum_threshold
  3. 按作用对象,VACUUM相关操作可作用于普通表、系统表、Toast表、分区表、索引、压缩表(ILM)等多种对象。不同对象对VACUUM、AUTOVACUUM、VACUUM FULL三种操作的支持情况如下表所示。
    表2 VACUUM对象类型

    对象类型

    VACUUM

    AUTOVACUUM

    VACUUM FULL

    普通表

    系统表

    Toast表

    EnToast表

    ×

    分区表

    二级分区表

    临时表

    ×

    全局临时表

    ×

    索引

    ×

    压缩表(ILM)

VACUUM与VACUUM FULL的区别

VACUUM与VACUUM FULL都是GaussDB中用于清理表空间、处理死元组的命令,二者的共同点在于都用于回收过时数据占用的空间、缓解表膨胀。

二者的核心差异在于清理方式与对业务的影响:普通VACUUM只将死元组空间标记为可重用,并不进行物理删除,也不把空间归还操作系统,过程中仅获取4级锁(ShareUpdateExclusiveLock),不影响读写;VACUUM FULL则会重建原表、物理删除死元组并将空间释放给操作系统,但执行期间会获取8级锁(AccessExclusiveLock),阻塞其他事务的读写访问。

这一差异决定了二者的适用场景不同:VACUUM是轻量级、不阻塞业务的常规清理手段,适合日常频繁执行;VACUUM FULL是重量级操作,执行时间随数据量增长,适合在业务低谷期对严重膨胀的表做彻底空间回收。

对比维度

VACUUM

VACUUM FULL

共同点

均用于清理死元组、缓解表膨胀。

清理方式

标记死元组空间为可重用,不物理删除。

重建原表,物理删除死元组。

空间归还

仅供库内复用,不归还操作系统。

将空间释放给操作系统。

是否锁表

获取4级锁(ShareUpdateExclusiveLock),不影响读写。

获取8级锁(AccessExclusiveLock),影响读写。

性能影响

轻量级,影响小。

重量级,影响大。

适用场景

日常常规清理。

业务低谷期彻底回收空间。

Astore VACUUM与Ustore VACUUM的区别

本节对比GaussDB新一代存储引擎Ustore与Astore在VACUUM机制和功能上的差异。Astore的堆表与Btree索引、Ustore的堆表与UB-tree索引(分为RCR与PCR两种格式)在AUTOVACUUM、VACUUM、VACUUM FULL三种操作下的功能支持情况如下三张表所示。

表3 表3 AUTOVACUUM功能对比

AUTOVACUUM

Astore

Btree

Ustore

Ubtree(RCR)

Ubtree(PCR)

扫描并删除行的无效版本

×

维护统计信息

×

×

×

维护FSM准确性

维护VM准确性

×

×

×

×

冻结过老的事务号

×

×

×

回收事务文件

释放物理空间

×

×

×

×

×

表4 表4 VACUUM功能对比

VACUUM

Astore

Btree

Ustore

Ubtree(RCR)

Ubtree(PCR)

扫描并删除行的无效版本

维护统计信息

维护FSM准确性

维护VM准确性

×

×

×

×

冻结过老的事务号

×

回收事务文件

释放物理空间

×

×

×

×

×

表5 表5 VACUUM FULL功能对比

VACUUM FULL

Astore

Btree

Ustore

Ubtree(RCR)

Ubtree(PCR)

释放物理存储空间

与VACUUM相关的特性和操作

在了解VACUUM的核心概念与分类后,如需了解更多细节以及在实际业务中配置使用,可参考以下对应章节:
  • 对表执行VACUUM:对指定表执行清理操作以回收空间,详情参考《管理员指南》中“导入数据 > 对表执行VACUUM”章节。
  • VACUUM语法:了解VACUUM/VACUUM FULL命令的完整语法与参数,详情参考《参考》中“SQL参考 > SQL语法 > V > VACUUM”章节。
  • 自动清理与参数配置:通过autovacuum、autovacuum_vacuum_scale_factor、autovacuum_vacuum_threshold等参数控制自动清理的开启与触发条件,详情参考《参考》中“数据库运行参数说明 > GUC参数说明 > 自动清理”章节及《性能调优》中“VACUUM/autovacuum介绍与调优 > autovacuum的相关参数”章节。
  • VACUUM/autovacuum调优:针对清理效率与膨胀控制进行优化,详情参考《性能调优》中“VACUUM/autovacuum介绍与调优”章节。

相关文档