概述
This new behavior reduces the work necessary when the table needs to be frozen and allows pages to be set as all-visible. All-visible pages allow index-only scans to access fewer heap rows.
为减少了需要冻结表时所需的工作量,并允许将页设置为全部可见。所有可见页都允许仅索引扫描访问较少的堆行。
官网中对该特性解释,理解上真的不是很直观,我来白话翻译下O(∩_∩)O~
在插入数据时可自动触发autovacumm,从而可以生成表的vm文件而无需手工触发vacumm,vm文件可以加快VACUUM清理的速度。
Inserted data can trigger autovacuum,为PostgreSQL13新增加的特性。
概念
VM
vacuum过程是一种维护过程,它的两个主要任务是删除死元组,以及冻结事物标识,由于清理过程设计全表扫描,因此该过程代价高昂,在PG8.4版本中引入了可见性映射(VM)文件来提高移除死元组的效率,在PG9.6版本中增强了VM,从而改善了冻结的过程。
FSM
插入堆或索引元组时,PostgreSQL使用表与索引相应的FSM来选择可供插入的页面。
表和索引都有各自的FSM,每个FSM存储着相应表或索引文件中每个页面可用空间容量的信息。
冻结
冻结的过程有两种模式,依特定条件而择其一执行。一种为惰性模式另一种为迫切模式。
惰性模式下,冻结过程仅适用目标表对应的VM扫描包含死元组的页面。
迫切模式会扫描所有的页面,无论其是否包含死元组,都会更新与冻结过程相关的系统视图,并在可能的情况下删除不必要的CLOG文件。
死元祖
当元组被修改时,元组会复制出多个版本(MVCC),当事物提交后,历史的版本对其它事物不可见,被标记为死元祖,等待autovacuum清理。死元组的产生也是因为PostgreSQL中没有单独的回滚段,而是将历史版本直接记录在数据页面中。
CLOG(XACT)
$PGDATA/pg_clog(xact)包含了事务的元数据。这种日志用于告诉PostgreSQL哪个事务已经完成、哪个还没有完成。clog是比较小的并且没有任何理由会膨胀,所以,你应该没有任何理由去碰触它。在任何时候你都不应该从pg_clog里删除文件,如果你这样子做,还不如完全地删除整个数据库目录。缺少clog是不可恢复的。请注意,这意味着,如果你在$PGDATA目录里备份文件,你应该确定同时包含pg_clog和pg_xlog,否则你可能会发现你的备份是不可用的。
VACUUM
并发清理
1.从指定的表中依次处理每一张表。
2.获取表上的ShareUpdateExclusiveLock锁,此锁允许其他事物对该表进行读取。
3.扫描表中所有的页面,以获取所有的死元组,并在必要时冻结旧的元组。
4.删除指向相应死元组的索引元组。
5.对表的每个页面执行步骤6和7中的操作。
6.删除死元组,并重新分配页面中的活元组。
7.更新目标表对应的FSM与VM。
8.如果最后一个页面没有任何元组,则截断最后一页。
9.更新与目标表清理过程相关的统计数据和系统视图。
10.更新与清理过程相关的统计数据和系统视图。
11.如果可能,移除CLOG中非必要的文件与页面。
完整清理
会移除整个文件中,所有的死元组,还会对整个文件中所有的活元组进行碎片整理。其他事物在完整清理运行时无法访问该表。
试验
实验基于PostgreSQL13.3
postgres=# create table test3(id int);
CREATE TABLE
insert into test4 select id from generate_series(1,1250) t(id);
INSERT 0 1250
postgres=# select pg_relation_filepath('test4');
pg_relation_filepath
----------------------
base/13580/17284
(1 row)
控制插入数据触发autovacuum的的阈值,默认值为1000
postgres=# show autovacuum_vacuum_insert_threshold;
autovacuum_vacuum_insert_threshold
------------------------------------
1000
(1 row)
控制插入数据时超过什么比例会触发autovacuum,默认值为20%
postgres=# show autovacuum_vacuum_insert_scale_factor;
autovacuum_vacuum_insert_scale_factor
---------------------------------------
0.2
(1 row)
postgres@bogon-> ll 17284*
-rw------- 1 postgres postgres 48K Jul 7 20:41 17284
-rw------- 1 postgres postgres 24K Jul 7 20:41 17284_fsm
-rw------- 1 postgres postgres 8.0K Jul 7 20:41 17284_vm
复制
代码导读
源码路径
src/backend/commands/vacuum.c
/*
* VACUUM和ANALYZE命令执行的主要入口点
*
* This is mainly a preparation wrapper for the real operations that will
* happen in vacuum().
*/
void
ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel){... ...}
/*
* Internal entry point for VACUUM and ANALYZE commands.
*
* 如果不是NIL,则为进程的VacuumRelation列表;否则,我们将处理数据库中所有相关的表。
* 对于每个VacuumRelation,如果提供了一个有效的OID,则该OID的表是要处理的,否则,VacuumRelation的RangeVar表示要处理的。
*
* params包含一组可用于自定义行为的参数。
*
* bstrategy通常被指定为NULL,但在autovacuum中它可以被传递在多个vacuum()调用中使用相同的缓冲区策略对象。
*
* isTopLevel应该从ProcessUtility传递下来。
*
* It is the caller's responsibility that all parameters are allocated in a
* memory context that will not disappear at transaction commit.
*/
void
vacuum(List *relations, VacuumParams *params,
BufferAccessStrategy bstrategy, bool isTopLevel){... ...}
/*
* vac_truncate_clog() -- attempt to truncate the commit log
*
* 扫描pg_database以确定系统范围内最早的datfrozenxid,并使用它来截断事务提交日志(pg_xact)。
* 还要更新由varsup.c维护的XID换行限制信息。同样datminmxid。
*
* 传递的frozenXID和minMulti是我自己的pg_database条目的更新值。它们用于初始化“min”计算。
* 调用者还传递“最后一个正常的”XID和MXID,因为它手边已经有了这些XID和MXID。
*
* 只有在我们成功更改了数据库的datfrozenxid/datminmxid值,
* 或者发现共享的XID-wrap-limit信息过期时才会被调用。
*/
static void
vac_truncate_clog(TransactionId frozenXID,
MultiXactId minMulti,
TransactionId lastSaneFrozenXid,
MultiXactId lastSaneMinMulti){... ...}
复制
参考
http://amitkapila16.blogspot.com/2020/05/improved-autovacuum-in-postgresql-13.html
https://www.postgresql.org/about/featurematrix/#backend
《PostgreSQL指南-内幕探索》