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与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)
- VACUUM
- 按触发方式,可分为上文已介绍的手动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
- 自动清理(autovacuum)
- 按作用对象,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三种操作下的功能支持情况如下三张表所示。
| AUTOVACUUM | Astore | Btree | Ustore | Ubtree(RCR) | Ubtree(PCR) |
|---|---|---|---|---|---|
| 扫描并删除行的无效版本 | √ | √ | × | √ | √ |
| 维护统计信息 | √ | √ | × | × | × |
| 维护FSM准确性 | √ | √ | √ | √ | √ |
| 维护VM准确性 | √ | × | × | × | × |
| 冻结过老的事务号 | √ | × | × | √ | × |
| 回收事务文件 | √ | √ | √ | √ | √ |
| 释放物理空间 | × | × | × | × | × |
| VACUUM | Astore | Btree | Ustore | Ubtree(RCR) | Ubtree(PCR) |
|---|---|---|---|---|---|
| 扫描并删除行的无效版本 | √ | √ | √ | √ | √ |
| 维护统计信息 | √ | √ | √ | √ | √ |
| 维护FSM准确性 | √ | √ | √ | √ | √ |
| 维护VM准确性 | √ | × | × | × | × |
| 冻结过老的事务号 | √ | × | √ | √ | √ |
| 回收事务文件 | √ | √ | √ | √ | √ |
| 释放物理空间 | × | × | × | × | × |
| VACUUM FULL | Astore | Btree | Ustore | Ubtree(RCR) | Ubtree(PCR) |
|---|---|---|---|---|---|
| 释放物理存储空间 | √ | √ | √ | √ | √ |
与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介绍与调优”章节。