【9】PostgreSQL 之 vacuum 死元组清理

PostgreSQL 之 vacuum 死元组清理

  • 前言
  • vacuum
    • [手动 vacuum](#手动 vacuum)
    • [自动 vacuum (autovacuum)](#自动 vacuum (autovacuum))

前言

初识 vacuum 先说下,什么是死元组?

前置说明:

表的死元组可通过查询 pg_stat_user_tables ,也有其他视图可查,当前不做过多说明。

例如:

有一张业务表 tb_client 其内有 5条 数据,此时做 1次 update 操作,将会产生 1个死元组。

sql 复制代码
--- (1) 业务表初始数据
postgres=# select * from tb_client;
  id  | name  
------+-------
 1001 | A1001
 1002 | B1002
 1003 | C1003
 1004 | D1004
 1005 | E1005
(5 rows)

postgres=# 

-- (2) 检查死元组情况
postgres=# select * from pg_stat_user_tables where relname = 'tb_client';
-[ RECORD 1 ]-------+----------
relid               | 24587
schemaname          | public
relname             | tb_client
seq_scan            | 1
seq_tup_read        | 5
idx_scan            | 
idx_tup_fetch       | 
n_tup_ins           | 5
n_tup_upd           | 0  --- 更新情况
n_tup_del           | 0
n_tup_hot_upd       | 0
n_live_tup          | 5
n_dead_tup          | 0   ---- 注意这里表示死元组数
n_mod_since_analyze | 5
last_vacuum         | 
last_autovacuum     | 
last_analyze        | 
last_autoanalyze    | 
vacuum_count        | 0
autovacuum_count    | 0
analyze_count       | 0
autoanalyze_count   | 0

postgres=# 


-- (3) 表数据,做 一次 Update
postgres=# update tb_client set name = '君九' where id = 1003;
UPDATE 1
postgres=# 


-- (4) 检查死元组情况
postgres=# select * from pg_stat_user_tables where relname = 'tb_client';
-[ RECORD 1 ]-------+----------
relid               | 24587
schemaname          | public
relname             | tb_client
seq_scan            | 2
seq_tup_read        | 10
idx_scan            | 
idx_tup_fetch       | 
n_tup_ins           | 5
n_tup_upd           | 1
n_tup_del           | 0
n_tup_hot_upd       | 1
n_live_tup          | 5
n_dead_tup          | 1   --- 死元组数为 1 了;
n_mod_since_analyze | 6
last_vacuum         | 
last_autovacuum     | 
last_analyze        | 
last_autoanalyze    | 
vacuum_count        | 0
autovacuum_count    | 0
analyze_count       | 0
autoanalyze_count   | 0

postgres=# 

? 思考

在文章最开始就说了:

有一张业务表 tb_client 其内有 5条 数据,此时做 1次 update 操作,将会产生 1个 死元组。
? 问题: 那同样的一条数据,如果做多次 update,那还会产生死元组吗?

演示:将 同一条数据, 做3次 update

sql 复制代码
postgres=# update tb_client set name = '君九' where id = 1003;
UPDATE 1
postgres=# update tb_client set name = '君九' where id = 1003;
UPDATE 1
postgres=# update tb_client set name = '君九' where id = 1003;
UPDATE 1
postgres=# 

此时,在查看死元组情况:

如下查看,发现 死元组 数又累计 3个。

sql 复制代码
postgres=# select * from pg_stat_user_tables where relname = 'tb_client';
-[ RECORD 1 ]-------+----------
relid               | 24587
schemaname          | public
relname             | tb_client
seq_scan            | 5
seq_tup_read        | 25
idx_scan            | 
idx_tup_fetch       | 
n_tup_ins           | 5
n_tup_upd           | 4
n_tup_del           | 0
n_tup_hot_upd       | 4
n_live_tup          | 5
n_dead_tup          | 4  --- 注意这里,死元组又 累计 3个。
n_mod_since_analyze | 9
last_vacuum         | 
last_autovacuum     | 
last_analyze        | 
last_autoanalyze    | 
vacuum_count        | 0
autovacuum_count    | 0
analyze_count       | 0
autoanalyze_count   | 0

postgres=# 

之所以此情况,和PostgreSQL数据存储有关,后续会说明,当前先 粗略理解 如下:

(a). 初始数据如下,放在一个数组里面。

(b). 当(update)修改 C1003君九数据时,并不是在原来的数据上修改的(即不是修改数组下标 2 的位置上直接修改),而是在数组下标 5 的位置上新增了一条数据。

?问题:那原来的数据(数组下标 2 )怎么办?

=》将其设置为不可见 (注意:是不可见,并不是删除。),这个不可见的数据,即死元组

当使用select 语句查询数据时,PostgreSQL底层会自动做处理让其 只能 查找可见的数据。
但是,

如果 你的 select 语句进行了全表扫 ,则会扫描到那些不可见的数据

例如:

若一张业务表,tb_client 有 100万 条数据,死元组数 有200万;

sql 复制代码
select * from tb_client where mobile = xxxx;

查询时,mobile 的索引刚好失效了,进而进行了全表扫,此时数据库在底层查询时扫描的数据是 100万 + 200万,共计300万。必然会影响查询性能

扩展:

死元组数过大也有可能会引起事务号回卷问题,后续会详细介绍,此处在不做过多说明。

先理解什么是死元组

那如何解决这些问题?

》必然是清理掉这些死元组,即当前章节说的 vacuum

vacuum

手动 vacuum

手动清理 死元组 即人工执行 vacuum 语句,其语法如下:

vacuum 语法:

sql 复制代码
postgres=# \h vacuum
Command:     VACUUM
Description: garbage-collect and optionally analyze a database
Syntax:
VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ table_name [ (column_name [, ...] ) ] ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ table_name ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ table_name [ (column_name [, ...] ) ] ]

postgres=# 

演示:

sql 复制代码
--- (1) 清理之前,查询一下 死元组数
postgres=# select * from pg_stat_user_tables where relname = 'tb_client';
-[ RECORD 1 ]-------+----------
relid               | 24587
schemaname          | public
relname             | tb_client
seq_scan            | 5
seq_tup_read        | 25
idx_scan            | 
idx_tup_fetch       | 
n_tup_ins           | 5
n_tup_upd           | 4
n_tup_del           | 0
n_tup_hot_upd       | 4
n_live_tup          | 5
n_dead_tup          | 4   ----- 注意这里:死元组数
n_mod_since_analyze | 9
last_vacuum         | 
last_autovacuum     | 
last_analyze        | 
last_autoanalyze    | 
vacuum_count        | 0
autovacuum_count    | 0
analyze_count       | 0
autoanalyze_count   | 0

postgres=# 

--- (2) vacuum 表名称;  进行清理
postgres=# vacuum tb_client;
VACUUM
postgres=#


--- (3) 再次查看死元组数
postgres=# select * from pg_stat_user_tables where relname = 'tb_client';
-[ RECORD 1 ]-------+------------------------------
relid               | 24587
schemaname          | public
relname             | tb_client
seq_scan            | 5
seq_tup_read        | 25
idx_scan            | 
idx_tup_fetch       | 
n_tup_ins           | 5
n_tup_upd           | 4
n_tup_del           | 0       ----- 注意这里,已清理为 零 
n_tup_hot_upd       | 4
n_live_tup          | 5
n_dead_tup          | 0
n_mod_since_analyze | 9
last_vacuum         | 2025-07-11 15:22:44.696469+08
last_autovacuum     | 
last_analyze        | 
last_autoanalyze    | 
vacuum_count        | 1
autovacuum_count    | 0
analyze_count       | 0
autoanalyze_count   | 0

postgres=# 

另外,vacuum 清理表死元组时,还可以添加一些额外参数(例如:full

vacuumvacuum full 区别:

》 扩展说明

  1. 标准 VACUUM
    空间处理 :只标记被删除/更新行占用的空间为"可重用",不会缩小物理文件大小
    并发性 :执行时不阻塞正常的读写操作
    用途

    • 维护可见性映射(visibility map)
    • 冻结旧的事务ID
    • 更新统计信息(配合ANALYZE)

    优势:可以在生产环境中频繁运行,影响小

  2. VACUUM FULL
    空间处理 :完全重组表,将空间返还给操作系统,显著减少表占用的磁盘空间
    并发性 :需要排它锁,会阻塞所有对该表的访问
    内部操作

    • 创建表的全新副本
    • 只拷贝有效数据到新文件
    • 删除原文件,用新文件替代

    风险

    • 长时间锁表
    • 执行期间需要额外空间(原始表大小的额外空间)
    • 可能造成WAL日志大量增长

自动 vacuum (autovacuum)

自动 vacuum 这些参数,可在 PostgreSQL 数据库中的 postgresql.conf 文件中配置。

autovacuum 相关参数如下,

sql 复制代码
postgres=# select name, setting from pg_settings where name like 'autovacuum%';
                name                 |  setting  
-------------------------------------+-----------
 autovacuum                          | on
 autovacuum_analyze_scale_factor     | 0.1
 autovacuum_analyze_threshold        | 50
 autovacuum_freeze_max_age           | 200000000
 autovacuum_max_workers              | 3
 autovacuum_multixact_freeze_max_age | 400000000
 autovacuum_naptime                  | 60
 autovacuum_vacuum_cost_delay        | 20
 autovacuum_vacuum_cost_limit        | -1
 autovacuum_vacuum_scale_factor      | 0.2
 autovacuum_vacuum_threshold         | 50
 autovacuum_work_mem                 | -1
(12 rows)

postgres=# 

参数说明

( 1 ) autovacuum (on/off ):是否启用自动 vacuum 进程,默认为 on

( 2 ) autovacuum_analyze_scale_factor(0.1 ):触发 ANALYZE 操作的表数据变化比例因子(相对于表大小),例如:100万行的表,当10万行(10%)发生变化时会触发ANALYZE。

( 3 ) autovacuum_analyze_threshold(50 ):触发 ANALYZE 操作的最小变更行数阈值,与scale_factor一起使用,取两者计算结果的较大值。

( 4 ) autovacuum_freeze_max_age(200000000):事务ID上限,超过此值强制进行vacuum以防止事务ID回卷 以事务数为单位

( 5 ) autovacuum_max_workers(3 ):可同时运行的最大autovacuum工作进程数

( 6 ) autovacuum_multixact_freeze_max_age(400000000 ):多事务ID上限,超过此值强制进行vacuum以防止多事务ID回卷

( 7 ) autovacuum_naptime(60 ):autovacuum进程两次运行之间的休眠时间(秒)

( 8 ) autovacuum_vacuum_cost_delay(20):当autovacuum达到cost限制时的延迟时间(毫秒)用于控制autovacuum对系统性能的影响。

( 9 ) autovacuum_vacuum_cost_limit(-1):autovacuum的cost限制,-1表示使用vacuum_cost_limit的值控制autovacuum的工作强度

( 10 ) autovacuum_vacuum_scale_factor(0.2):触发VACUUM操作的表数据变化比例因子(相对于表大小)例如:100万行的表,当20万行(20%)发生变化时会触发VACUUM

( 11 ) autovacuum_vacuum_threshold (50):触发VACUUM操作的最小变更行数阈值,与scale_factor一起使用,取两者计算结果的较大值。

( 12 ) autovacuum_work_mem(-1):每个autovacuum工作进程可用的内存量,-1表示使用maintenance_work_mem的值

上述的参数中,可看以下参数:

sql 复制代码
--- vacuum 清理死元组
 autovacuum_vacuum_scale_factor      | 0.2
 autovacuum_vacuum_threshold         | 50
 ---- analyze 收集统计信息
 autovacuum_analyze_scale_factor     | 0.1
 autovacuum_analyze_threshold        | 50

autovacuum_vacuum_scale_factor 参数设定为 0.2(20%),表示当表中 20% 的数据被修改时触发 VACUUM 操作。但这一机制存在一个明显问题:对于小表来说,20% 的修改比例可能仅对应几行数据。
例如
百万行大表 = 20万行修改 → 此时,占比 20% ,值得清理
单行小表: = 1行修改 → 此时,占比 100%,但是,不值得清理

为解决这个问题,PostgreSQL 引入了 autovacuum_vacuum_threshold 参数(默认50行),形成双重触发条件:
(1)必须达到 20% 的修改比例 且
(2)修改行数至少达到 50 行

这种设计有效避免了小表频繁触发不必要的 VACUUM 操作。

相关推荐
打鱼又晒网6 分钟前
Lecture #20:Database Logging
数据库
白仑色9 分钟前
Oracle 数据库管理与维护实战指南(用户权限、备份恢复、性能调优)
数据库·oracle·数据库管理·性能调优·备份恢复
wx_ywyy679813 分钟前
分布式推客系统全栈开发指南:SpringCloud+Neo4j+Redis实战解析
数据库·oracle·推客系统·推客小程序·推客系统开发·推客小程序开发·推客分销系统
isNotNullX20 分钟前
实时数仓和离线数仓还分不清楚?看完就懂了
大数据·数据库·数据仓库·人工智能·数据分析
怀君25 分钟前
Flutter——数据库Drift开发详细教程之迁移(九)
数据库·flutter
周杰伦的稻香26 分钟前
mysql_multi多实例管理
数据库·mysql
kk在加油35 分钟前
Redis基础数据结构
数据结构·数据库·redis
只有干货43 分钟前
dexie 前端数据库封装
数据库
HardCodeV1 小时前
NestJS
数据库·oracle
花好月圆春祺夏安2 小时前
基于odoo17的设计模式详解---备忘模式
数据库·设计模式