一、 作用与原理
page pruning执行速度很快,但它们的作用范围毕竟只有单页、且不包含索引,因此,我们还需要更有效的清理机制。
常规vacuum是最常用的一种,作用范围可以是整张表,清理过期元组及索引项,并且不阻塞读和写。为提高效率,vacuum会结合前面提到的.vm文件,跳过不需清理的页。清理之后,还会更新前面提到的fsm(空闲空间映射)文件。
另外还可以用vacuum analyze顺便收集统计信息。
二、 vacuum案例
CREATE TABLE vac(id integer,s char(100)) WITH (autovacuum_enabled = off);
CREATE INDEX vac_s ON vac(s);
INSERT INTO vac(id,s) VALUES (1,'A');
UPDATE vac SET s = 'B';
UPDATE vac SET s = 'C';
SELECT * FROM heap_page('vac',0);
SELECT * FROM index_page('vac_s',1);
Vacuum基于database horizon检查和清理表和索引中的死元组,尚未过期的元组不能清理。
VACUUM vac;
SELECT * FROM heap_page('vac',0);
SELECT * FROM index_page('vac_s',1);
Vacuum之后pg会为vac表创建vm和fsm文件
安装插件可以看到某页中的元组是否均可见
CREATE EXTENSION if not exists pg_visibility;
SELECT all_visible FROM pg_visibility_map('vac',0);
如果不想安装,从页头信息也可以看到
SELECT flags & 4 > 0 AS all_visible FROM page_header(get_raw_page('vac',0));
三、 Vacuum阶段
1. 查看vacuum详情
postgres=# VACUUM VERBOSE vac;
INFO: vacuuming "public.vac"
INFO: scanned index "vac_s" to remove 1 row versions
DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
INFO: table "vac": removed 1 dead item identifiers in 1 pages
DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
INFO: index "vac_s" now contains 2 row versions in 2 pages
DETAIL: 1 index row versions were removed.
0 index pages were newly deleted.
0 index pages are currently deleted, of which 0 are currently reusable.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
INFO: table "vac": found 1 removable, 2 nonremovable row versions in 1 out of 1 pages
DETAIL: 1 dead row versions cannot be removed yet, oldest xmin: 808
Skipped 0 pages due to buffer pins, 0 frozen pages.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
VACUUM
它列出了扫描的表和索引、cpu及耗时、删除了多少项、不能删除的有多少项、database horizon(红色部分)、跳过多少项、冻结多少个页,非常详细,根据它也可以大致推断vacuum的执行阶段。
查看vacuum进度
SELECT * FROM pg_stat_progress_vacuum \gx
2. 主要阶段
1)表扫描
- 读取vm文件,跳过其中页面
- 根据database horizon扫描死元组,并将还需要被索引引用的元组id加入一个特殊的tid数组,这些元组暂时不能清理
- 这部分可用内存取决于maintenance_work_mem
2)索引vacuum
- 表上的所有索引被完整扫描,找到引用tid数组中元组的对应索引项
- 从索引页中删除这些索引项
- 如果索引大小超过min_parallel_index_scan_size,将会启用并行
- 更新fsm文件并收集统计信息
- 如果只对表进行过insert,会直接跳过本阶段
3)表vacuum
- 删除tid数组中的元组及对应指针(此时所有索引引用已被删除)
- 更新fsm和vm文件
- 清理tid数组
4)表截断(heap truncation)
- 特殊场景:如果清空的页刚好在文件末尾,vacuum可以将这些空间归还给操作系统
- heap truncation会短暂持有排它锁
- 由于排它锁会影响业务,pg只在文件末尾有至少1000个页,或者达到表大小的1/16时,才执行截断。这两个参数是硬编码,不能修改。
- 如果确实怕被排它锁影响业务,可以在表级设置vacuum_truncate和toast.vacuum_truncate参数,禁用该功能:ALTER TABLE some_table SET (vacuum_truncate = off);
未完待续...