清理过程为指定的表,或数据库中的所有表执行以下任务。
- 移除死元组
- 移除每一页中的死元组,并对每一页内的活元组进行碎片整理。
- 移除指向死元组的索引元组。
- 冻结旧的事务标识(
txid
)- 如有必要,冻结旧元组的事务标识(txid)。
- 更新与冻结事务标识相关的系统视图(
pg_database
与pg_class
)。 - 如果可能,移除非必需的提交日志(clog)。
- 其他
- 更新已处理表的空闲空间映射(FSM)和可见性映射(VM)。
- 更新一些统计信息(
pg_stat_all_tables
等)。
这里假设读者已经熟悉以下术语:死元组,冻结事务标识,FSM,clog;如果读者不熟悉这些术语的含义,请参阅 第五章 并发控制 。VM将在 6.2 可见性映射 中介绍。以下伪代码描述了清理的过程。
伪码:并发清理
(1) FOR each table (2) 在目标表上获取 ShareUpdateExclusiveLock 锁 /* 第一部分 */ (3) 扫描所有页面,定位死元组;如有必要,冻结过老的元组。 (4) 如果存在,移除指向死元组的索引元组。 /* 第二部分 */ (5) FOR each page of the table (6) 移除死元组,重排本页内的活元组。 (7) 更新 FSM 与 VM END FOR /* 第三部分 */ (8) 如果可能,截断最后的页面。 (9) 更新系统数据字典与统计信息 释放ShareUpdateExclusiveLock锁 END FOR /* 后续处理 */ (10) 更新统计信息与系统数据字典 (11) 如果可能,移除没有必要的文件,以及clog中的文件。
- 从指定的表集中依次处理每一张表。
- 获取表上的
ShareUpdateExclusiveLock
锁, 此锁允许其他事务对该表进行读取。- 扫描表中所有的页面,以获取所有的死元组,并在必要时冻结旧元组。
- 删除指向相应死元组的索引元组(如果存在)。
- 对表的每个页面执行步骤(6)和(7)中的操作
- 移除死元组,并重新分配页面中的活元组。
- 更新目标表对应的FSM与VM。
- 如果最后一个页面没有任何元组,则截断最后一页。
- 更新与目标表 清理过程相关的统计数据和系统视图。
- 更新与清理过程相关的统计数据和系统视图。
- 如果可能,移除clog中非必需的文件与页面。
该伪码分为两大块:一块是依次处理表的循环,一块是后处理逻辑。而循环块又能分为三个部分,每一个部分都有各自的任务。接下来会描述这三个部分,以及后处理的逻辑。
6.1.1 第一部分
这一部分执行冻结处理,并删除指向死元组的索引元组。
首先,PostgreSQL扫描目标表以构建死元组列表,如果可能的话,还会冻结旧元组。该列表存储在本地内存中的maintenance_work_mem
里(维护用的工作内存)。冻结处理将在第6.3节中介绍。
扫描完成后,PostgreSQL根据构建得到的死元组列表来删除索引元组。该过程在内部被称为“清除阶段(cleanup stage) ”。不用说,该过程代价高昂。在10或更早版本中始终会执行清除阶段。在11或更高版本中,如果目标索引是B树,是否执行清除阶段由配置参数vacuum_cleanup_index_scale_factor
决定。详细信息请参考此参数的说明。
当maintenance_work_mem
已满,且未完成全部扫描时,PostgreSQL继续进行后续任务,即步骤4到7;完成后再重新返回步骤3并继续扫描。
6.1.2 第二部分
这一部分会移除死元组,并逐页更新FSM和VM。图6.1展示了一个例子:
图6.1 删除死元组 假设该表包含三个页面,这里先关注0号页面(即第一个页面)。该页面包含三条元组, 其中Tuple_2
是一条死元组,如图6.1(1)所示。在这里PostgreSQL移除了Tuple_2
,并重排剩余元组来整理碎片空间,然后更新该页面的FSM和VM,如图6.1(2)所示。 PostgreSQL不断重复该过程直至最后一页。
请注意,非必需的行指针是不会被移除的,它们会在将来被重用。因为如果移除了行指针,就必须同时更新所有相关索引中的索引元组。
6.1.3 第三部分
第三部分会针对每个表,更新与清理过程相关的统计信息和系统视图。此外,如果最后一页中没有元组,则该页会从表文件中被截断。
6.1.4 后续处理
当处理完成后,PostgreSQL会更新与清理过程相关的几个统计数据,以及相关的系统视图;如果可能的话,它还会移除部分非必需的clog( 6.4 移除不必要的提交日志文件 )。
清理过程使用8.5节中将描述的环形缓冲区(ring buffer) 。因此处理过的页面不会缓存在共享缓冲区中。
下一节:清理过程的代价高昂,因此PostgreSQL在8.4版中引入了VM,用于减小清理的开销。