PostgreSQL 数据库需要称为清理的定期维护。对于许多安装来说,由自动清理守护进程执行清理就足够了,该守护进程在第 24.1.6 节中进行了描述。您可能需要调整其中描述的自动清理参数,以获得针对您情况的最佳结果。一些数据库管理员可能希望通过手动管理的 VACUUM
命令来补充或替代守护进程的活动,这些命令通常会根据计划由 cron 或 Task Scheduler 脚本执行。要正确设置手动管理的清理,理解接下来的几个小节中讨论的问题至关重要。依赖自动清理的管理员可能仍希望快速浏览这些材料,以帮助他们理解和调整自动清理。
PostgreSQL 的 VACUUM
命令需要定期处理每个表,原因如下:
这些原因中的每一个都决定了执行不同频率和范围的 VACUUM
操作,如以下小节所述。
VACUUM
有两个变体:标准 VACUUM
和 VACUUM FULL
。 VACUUM FULL
可以回收更多磁盘空间,但运行速度慢得多。此外,标准形式的 VACUUM
可以与生产数据库操作并行运行。(诸如 SELECT
、INSERT
、UPDATE
和 DELETE
等命令将继续正常工作,但当表被清理时,您将无法使用诸如 ALTER TABLE
等命令修改表的定义。) VACUUM FULL
需要对正在处理的表进行 ACCESS EXCLUSIVE
锁,因此无法与其他表的使用并行进行。因此,通常情况下,管理员应努力使用标准 VACUUM
并避免 VACUUM FULL
。
VACUUM
会产生大量的 I/O 流量,这可能导致其他活动会话的性能下降。有一些配置参数可以调整以减少后台清理的性能影响 — 请参阅第 19.10.2 节。
在 PostgreSQL 中,行的 UPDATE
或 DELETE
操作不会立即删除行的旧版本。此方法对于获得多版本并发控制 (MVCC) 的好处是必需的(MVCC参见第 13 章):只要行版本仍然可能对其他事务可见,就不能删除它。但最终,过时或已删除的行版本将不再被任何事务关注。然后必须回收它占用的空间以供新行重用,以避免磁盘空间需求的无限增长。这通过运行 VACUUM
来完成。
标准形式的 VACUUM
会移除表和索引中的死行版本,并将空间标记为可供将来重用。但是,它不会将空间返回给操作系统,除非在一种特殊情况下,即表中一个或多个页面完全变为空闲,并且可以轻松获取排他性表锁。相反,VACUUM FULL
通过写入一个没有死空间的完整的新表文件版本来主动压缩表。这最小化了表的大小,但可能需要很长时间。它还为新表副本需要额外的磁盘空间,直到操作完成。
例行清理的通常目标是足够频繁地执行标准 VACUUM
操作,以避免需要 VACUUM FULL
。自动清理守护进程试图以这种方式工作,事实上,它永远不会发出 VACUUM FULL
。在此方法中,其思想不是将表保持在其最小尺寸,而是保持稳定的磁盘空间使用:每个表占用的空间相当于其最小尺寸加上清理运行之间使用的空间。虽然 VACUUM FULL
可用于将表缩小回其最小尺寸并向操作系统返回磁盘空间,但如果表将来会再次增长,那么这样做意义不大。因此,对于维护大量更新的表,中等频率的标准 VACUUM
运行比不频繁的 VACUUM FULL
运行更好。
一些管理员更喜欢自己安排清理,例如在低负载的夜间完成所有工作。根据固定时间表进行清理的困难在于,如果一个表意外地更新活动激增,它可能会变得肿胀到实际上需要 VACUUM FULL
来回收空间。使用自动清理守护进程可以缓解这个问题,因为守护进程会根据更新活动动态地安排清理。除非您有高度可预测的工作负载,否则完全禁用守护进程是不明智的。一种可能的折衷是设置守护进程的参数,使其仅对异常重的更新活动做出响应,从而防止事情失控,而计划的 VACUUM
操作则负责在典型负载下完成大部分工作。
对于不使用自动清理的管理员来说,一种典型的方法是在低使用期安排每天一次数据库范围的 VACUUM
,并根据需要补充对大量更新的表的更频繁的清理。(一些更新率极高的安装会像每隔几分钟一样频繁地清理其最繁忙的表。)如果您在一个集群中有多个数据库,请不要忘记 VACUUM
每个数据库;程序 vacuumdb 可能会有所帮助。
当一个表因大量更新或删除活动而包含大量死行版本时,普通的 VACUUM
可能不令人满意。如果您有这样的表并且需要回收它占用的过多磁盘空间,您将需要使用 VACUUM FULL
,或者改用 CLUSTER
或 ALTER TABLE
的某个表重写变体。所有这些命令都会重写表的一个全新副本并为其构建新索引。所有这些选项都需要 ACCESS EXCLUSIVE
锁。请注意,在操作完成之前,旧表和索引副本无法被释放,因此它们也会暂时使用大约等于表大小的额外磁盘空间。
如果您有一个表在其全部内容被定期删除,请考虑使用 TRUNCATE
而不是使用 DELETE
后跟 VACUUM
。 TRUNCATE
会立即删除表的所有内容,而无需后续的 VACUUM
或 VACUUM FULL
来回收现在未使用的磁盘空间。缺点是严格的 MVCC 语义被违反了。
为了生成良好的查询计划,PostgreSQL 查询优化器依赖于表内容的统计信息。这些统计信息由 ANALYZE
命令收集,该命令可以单独调用,也可以作为 VACUUM
的可选步骤。拥有相对准确的统计信息很重要,否则糟糕的计划选择可能会降低数据库性能。
如果启用了自动清理守护进程,它将在表内容发生足够大的变化时自动发出 ANALYZE
命令。但是,管理员可能更喜欢依赖手动计划的 ANALYZE
操作,特别是当已知对表的更新活动不会影响“有趣”列的统计信息时。守护进程严格地将 ANALYZE
的计划安排为插入或更新的行数的一个函数;它不知道这是否会导致有意义的统计变化。
分区和继承子表中的元组更改不会触发父表的分析。如果父表为空或很少更改,它可能永远不会被自动清理处理,并且继承树整体的统计信息也不会被收集。因此,有必要手动运行 ANALYZE
来保持统计信息最新。
与清理空间回收一样,对于大量更新的表,频繁更新统计信息比很少更新的表更有用。但即使对于大量更新的表,如果数据的统计分布变化不大,也可能不需要更新统计信息。一个简单的经验法则是考虑表中列的最小值和最大值变化的程度。例如,包含行更新时间的 timestamp
列将随着行的添加和更新而具有不断增加的最大值;这样的列可能比包含网站访问页面 URL 的列需要更频繁的统计信息更新。URL 列可能接收相同的更改频率,但其值的统计分布可能变化相对缓慢。
可以对特定表甚至表的特定列运行 ANALYZE
,因此如果您的应用程序需要,可以提供更频繁地更新某些统计信息的灵活性。但在实践中,最好只分析整个数据库,因为它是一个快速操作。ANALYZE
使用表中行的统计随机样本,而不是读取每一行。
虽然对 ANALYZE
频率进行逐列调整可能效果不大,但您可能会发现逐列调整 ANALYZE
收集的统计信息详细程度是值得的。在 WHERE
子句中大量使用且数据分布高度不规则的列可能比其他列需要更精细的数据直方图。请参阅 ALTER TABLE SET STATISTICS
,或使用 default_statistics_target 配置参数更改数据库范围的默认值。
此外,默认情况下,关于函数选择性的信息有限。但是,如果您创建了使用函数调用的统计对象或表达式索引,将收集关于该函数的信息,这可以极大地改进使用表达式索引的查询计划。
自动清理守护进程不会对外部表发出 ANALYZE
命令,因为它无法确定何时可能有用。如果您的查询需要外部表的统计信息来进行正确规划,最好按照合适的计划手动运行这些表的 ANALYZE
命令。
自动清理守护进程不会对分区表发出 ANALYZE
命令。仅当父表本身发生更改时,继承父表才会被分析 — 子表的更改不会触发父表的自动分析。如果您的查询需要父表的统计信息来进行正确规划,有必要定期对手动运行这些表的 ANALYZE
以保持统计信息最新。
清理程序为每个表维护一个可见性图,以跟踪哪些页面仅包含已知对所有活动事务(以及直到页面再次被修改之前的所有未来事务)可见的元组。这有两个目的。首先,清理程序本身可以在下一次运行时跳过这些页面,因为没有什么需要清理。
其次,它允许 PostgreSQL 仅使用索引回答某些查询,而不引用底层表。由于 PostgreSQL 索引不包含元组可见性信息,因此正常索引扫描会为每个匹配的索引条目获取堆元组,以检查它是否应该被当前事务看到。另一方面,仅索引扫描会先检查可见性图。如果已知页面上的所有元组都可见,则可以跳过堆提取。这在可见性图可以防止磁盘访问的大型数据集上非常有用。可见性图远小于堆,因此即使堆非常大,它也可以轻松缓存。
PostgreSQL 的MVCC事务语义依赖于能够比较事务 ID(XID)编号:插入 XID 大于当前事务 XID 的行版本是“未来”的,并且不应对当前事务可见。但是,由于事务 ID 的大小有限(32 位),长时间运行(超过 40 亿次事务)的集群将遭受事务 ID 回绕:XID 计数器回绕到零,并且突然间曾经过去的事务现在看起来像是未来的事务——这意味着它们的结果变得不可见。简而言之,灾难性的数据丢失。(实际上数据仍然存在,但如果您无法访问它,那也无济于事。)为了避免这种情况,必须至少每 20 亿次事务清理每个数据库中的每个表。
定期清理可以解决问题的原因为 VACUUM
会将行标记为冻结,表示它们是由已提交的足够远的事务插入的,以至于插入事务的效果肯定对所有当前和未来的事务都可见。正常 XID 使用模 232 算术进行比较。这意味着对于每个正常 XID,都有 20 亿个 XID 比它“旧”,另外 20 亿个 XID 比它“新”;换句话说,正常 XID 空间是循环的,没有端点。因此,一旦创建了一个具有特定正常 XID 的行版本,该行版本在接下来的 20 亿次事务中都会显得“过去”,无论我们指的是哪个正常 XID。如果该行版本在超过 20 亿次事务后仍然存在,它将突然显得“未来”。为了防止这种情况,PostgreSQL 保留了一个特殊的 XID,FrozenTransactionId
,它不遵循正常的 XID 比较规则,并且始终被认为比任何正常 XID 都旧。冻结的行版本被视为好像插入 XID 是 FrozenTransactionId
,因此无论它们存在多久,它们对所有正常事务都会显得“过去”,并且不受回绕问题的影响。因此,这些行版本将保持有效,直到被删除,无论那需要多久。
在 PostgreSQL 9.4 之前的版本中,通过实际用 FrozenTransactionId
替换行的插入 XID 来实现冻结,这在行的 xmin
系统列中可见。较新版本仅设置一个标志位,保留行的原始 xmin
以供可能的取证使用。但是,在从 9.4 之前的版本升级的数据库中,仍然可以找到 xmin
等于 FrozenTransactionId
(2) 的行。
此外,系统目录可能包含 xmin
等于 BootstrapTransactionId
(1) 的行,表示它们是在 initdb 的第一个阶段插入的。与 FrozenTransactionId
一样,这个特殊的 XID 被认为比任何正常 XID 都旧。
vacuum_freeze_min_age 控制 XID 值需要多旧才能冻结带有该 XID 的行。增加此设置可能会避免不必要的工作,如果原本要冻结的行很快就会被修改,但减少此设置会增加表在必须再次清理之前可以经过的事务数。
VACUUM
使用可见性图来确定必须扫描表的哪些页面。通常,即使这些页面可能仍包含具有旧 XID 值的行版本,它也会跳过没有任何死行版本的页面。因此,常规 VACUUM
不会始终冻结表中的所有旧行版本。当这种情况发生时,VACUUM
最终需要执行强制清理,它将冻结所有符合条件的未冻结 XID 和 MXID 值,包括那些来自全部可见但并非全部冻结的页面。
如果一个表正在累积一个所有页面都可见但并非所有页面都冻结的积压,一个常规清理可能会选择扫描可跳过的页面以尝试冻结它们。这样做会减少下一次强制清理必须扫描的页面数量。这些被称为急切扫描的页面。可以通过增加 vacuum_max_eager_freeze_failure_rate 来调整急切扫描,以尝试冻结更多所有页面都可见的页面。即使急切扫描已将所有页面可见但并非全部冻结的页面数量降至最低,大多数表仍需要定期强制清理。但是,任何成功急切冻结的页面都可以在强制清理期间被跳过,因此急切冻结可能会最小化强制清理的开销。
vacuum_freeze_table_age 控制何时强制清理一个表。如果自上次此类扫描以来经过的事务数大于 vacuum_freeze_table_age
减去 vacuum_freeze_min_age
,则扫描所有页面可见但并非全部冻结的页面。将 vacuum_freeze_table_age
设置为 0 会强制 VACUUM
始终使用其强制策略。
一个表可以不被清理的最长时间是 20 亿个事务减去上次强制清理时的 vacuum_freeze_min_age
值。如果它超过这个时间未被清理,可能会导致数据丢失。为了确保这种情况不会发生,当数据库中最旧的 XID 距离回绕点只有 autovacuum_freeze_max_age 事务时,就会自动调用 autovacuum。 (即使 autovacuum 被禁用,这也会发生。)
这意味着,如果一个表没有被其他方式清理,它将在大约每 autovacuum_freeze_max_age
减去 vacuum_freeze_min_age
个事务后调用 autovacuum。对于定期因空间回收而清理的表来说,这无关紧要。然而,对于静态表(包括接收插入但没有或几乎没有更新或删除操作的表),则不需要为了回收空间而进行清理,因此尝试最大化强制 autovacuum 对非常大的静态表的间隔是有益的。显然,可以通过增加 autovacuum_freeze_max_age
或减少 vacuum_freeze_min_age
来实现这一点。
vacuum_freeze_table_age
的有效最大值是 0.95 * autovacuum_freeze_max_age
;大于此值的设置将被限制为最大值。大于 autovacuum_freeze_max_age
的值没有意义,因为到那时将触发一个反回绕 autovacuum,并且 0.95 的乘数留有一些缓冲空间,可以在此之前运行手动 VACUUM
。经验法则规定,vacuum_freeze_table_age
应设置为略低于 autovacuum_freeze_max_age
的值,留出足够的间隔,以便一个定期计划的 VACUUM
或由正常删除和更新活动触发的 autovacuum 在此窗口中运行。将其设置得太近可能会导致反回绕 autovacuum,即使表最近被清理以回收空间,而较低的值会导致更频繁的强制清理。
增加 autovacuum_freeze_max_age
(以及 vacuum_freeze_table_age
) 的唯一缺点是 pg_xact
和 pg_commit_ts
子目录的数据库集群将占用更多空间,因为它必须存储从 autovacuum_freeze_max_age
范围回溯到所有事务的提交状态(如果启用了 track_commit_timestamp
)和时间戳。提交状态每个事务使用两位,因此如果 autovacuum_freeze_max_age
设置为其允许的最大值 20 亿,那么 pg_xact
预计会增长到大约半个 GB,而 pg_commit_ts
会增长到大约 20GB。如果这与您的总数据库大小相比微不足道,则建议将 autovacuum_freeze_max_age
设置为其允许的最大值。否则,根据您愿意为 pg_xact
和 pg_commit_ts
存储分配多少空间来设置它。(默认值 2 亿事务,相当于大约 50MB 的 pg_xact
存储和大约 2GB 的 pg_commit_ts
存储。)
减少 vacuum_freeze_min_age
的一个缺点是它可能会导致 VACUUM
做无用功:如果行很快被修改(导致它获得新的 XID),那么冻结行版本就是浪费时间。因此,设置应该足够大,以至于行不太可能再次更改。
为了跟踪数据库中最旧未冻结 XID 的年龄,VACUUM
将 XID 统计信息存储在系统表 pg_class
和 pg_database
中。特别是,表 pg_class
行的 relfrozenxid
列包含在上次成功推进 relfrozenxid
的 VACUUM
(通常是上次强制 VACUUM)结束时仍然存在的最旧未冻结 XID。类似地,数据库 pg_database
行的 datfrozenxid
列是该数据库中出现的未冻结 XID 的下界 — 它只是数据库中每个表 relfrozenxid
值的最小值。检查此信息的便捷方法是执行如下查询:
SELECT c.oid::regclass as table_name, greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age FROM pg_class c LEFT JOIN pg_class t ON c.reltoastrelid = t.oid WHERE c.relkind IN ('r', 'm'); SELECT datname, age(datfrozenxid) FROM pg_database;
age
列测量从截止 XID 到当前事务 XID 的事务数。
当指定 VACUUM
命令的 VERBOSE
参数时,VACUUM
会打印有关表的各种统计信息。这包括有关 relfrozenxid
和 relminmxid
如何推进以及新冻结页数的信息。当自动清理日志记录(由 log_autovacuum_min_duration 控制)报告由自动清理执行的 VACUUM
操作时,相同的详细信息会出现在服务器日志中。
虽然 VACUUM
主要扫描自上次清理以来已修改的页面,但它也可能急切地扫描一些所有页面都可见但并非全部冻结的页面,试图冻结它们,但 relfrozenxid
仅在扫描完表中可能包含未冻结 XID 的每个页面后才会被推进。当 relfrozenxid
比 vacuum_freeze_table_age
事务旧时,当使用 VACUUM
的 FREEZE
选项时,或者当所有尚未全部冻结的页面碰巧需要清理以移除死行版本时,就会发生这种情况。当 VACUUM
扫描表中所有尚未全部冻结的页面时,它应该将 age(relfrozenxid)
设置为一个略大于 vacuum_freeze_min_age
设置的值(更多的是自 VACUUM
开始以来启动的事务数)。VACUUM
会将 relfrozenxid
设置为表中剩余的最旧 XID,因此最终值可能比严格要求的要新得多。如果在表中未发出 relfrozenxid
推进的 VACUUM
直到达到 autovacuum_freeze_max_age
,那么很快就会强制对该表进行 autovacuum。
如果由于某种原因 autovacuum 未能清除表中的旧 XID,当数据库的最旧 XID 距离回绕点还有 4000 万事务时,系统将开始发出类似以下的警告消息:
WARNING: database "mydb" must be vacuumed within 39985967 transactions HINT: To avoid XID assignment failures, execute a database-wide VACUUM in that database.
(如提示所示,手动 VACUUM
应能解决问题;但请注意,VACUUM
应由超级用户执行,否则它将无法处理系统目录,这将阻止它能够推进数据库的 datfrozenxid
。) 如果忽略这些警告,一旦距离回绕只剩下三百万事务,系统将拒绝分配新的 XID。
ERROR: database is not accepting commands that assign new XIDs to avoid wraparound data loss in database "mydb" HINT: Execute a database-wide VACUUM in that database.
在此条件下,所有正在进行的事务都可以继续,但只能启动只读事务。修改数据库记录或截断关系的操作将失败。VACUUM
命令仍然可以正常运行。请注意,与早期版本有时推荐的相反,在这种情况下,停止 postmaster 或进入单用户模式以恢复正常操作是不必要的,也是不推荐的。相反,请按照以下步骤操作:
age(transactionid)
较大的行。这些事务应被提交或回滚。age(backend_xid)
或 age(backend_xmin)
较大的行来查找它们。这些事务应被提交或回滚,或者会话可以使用 pg_terminate_backend
终止。age(xmin)
或 age(catalog_xmin)
较大的槽。在许多情况下,这些槽是为不再存在的服务器或已长时间关闭的服务器的复制而创建的。如果您为仍然存在且可能仍尝试连接到该槽的服务器删除一个槽,那么该副本可能需要重建。VACUUM
。数据库范围的 VACUUM
是最简单的;为了减少所需时间,也可以对 relminxid
最旧的表发出手动 VACUUM
命令。在此场景下不要使用 VACUUM FULL
,因为它需要一个 XID,因此会失败,除非在超级用户模式下,它将消耗一个 XID,从而增加事务 ID 回绕的风险。也不要使用 VACUUM FREEZE
,因为它将执行比恢复正常运行所需的最小工作量更多的工作。在早期版本中,有时需要停止 postmaster 并以单用户模式 VACUUM
数据库。在典型场景中,这不再是必需的,并且应尽可能避免,因为它涉及到系统停机。它也更危险,因为它会禁用旨在防止数据丢失的事务 ID 回绕保护。在此场景下使用单用户模式的唯一原因是,如果您希望 TRUNCATE
或 DROP
不需要表以避免需要 VACUUM
它们。三百万事务的安全裕度是为了让管理员完成这项工作。有关使用单用户模式的详细信息,请参阅 postgres 参考页。
多事务 ID 用于支持多个事务的行锁定。由于元组头中用于存储锁定信息的空间有限,当有多个事务同时锁定一个行时,该信息将被编码为一个“多事务 ID”,简称 multixact ID。关于哪些事务 ID 包含在任何特定 multixact ID 中的信息存储在 pg_multixact
子目录中,并且只有 multixact ID 出现在元组头中的 xmax
字段中。与事务 ID 一样,multixact ID 实现为 32 位计数器和相应的存储,所有这些都需要仔细的年龄管理、存储清理和回绕处理。有一个单独的存储区域,其中包含每个 multixact 的成员列表,它也使用 32 位计数器,并且也必须进行管理。系统函数 pg_get_multixact_members()
(在表 9.84中描述)可用于检查与 multixact ID 关联的事务 ID。
每当 VACUUM
扫描表的一部分时,它都会将遇到的任何旧于 vacuum_multixact_freeze_min_age 的 multixact ID 替换为另一个值,该值可以是零值、单个事务 ID 或较新的 multixact ID。对于每个表,pg_class
.relminmxid
存储该表中出现的任何元组中的最旧可能 multixact ID。如果此值比 vacuum_multixact_freeze_table_age 旧,则会强制执行强制清理。如上一节所述,强制清理意味着将跳过仅那些已知为全部冻结的页面。mxid_age()
可用于 pg_class
.relminmxid
来查找其年龄。
强制 VACUUM
,无论是什么原因导致的,都保证能够推进表的 relminmxid
。最终,随着所有数据库中的所有表都被扫描并推进其最旧的 multixact 值,可以移除旧 multixacts 的磁盘存储。
作为安全措施,对于 multixact 年龄大于 autovacuum_multixact_freeze_max_age 的任何表,都会发生强制清理扫描。此外,如果 multixact 成员占用的存储空间超过约 10GB,则会对所有表更频繁地执行强制清理扫描,从 multixact 年龄最老的表开始。这两种强制扫描都会发生,即使 autovacuum 名义上被禁用。成员存储区最多可以增长到约 20GB,然后达到回绕点。
与 XID 情况类似,如果 autovacuum 未能清除表中的旧 MXID,当数据库的最旧 MXID 距离回绕点还有 4000 万事务时,系统将开始发出警告消息。而且,就像在 XID 情况一样,如果忽略这些警告,一旦距离回绕只剩下三百万个,系统将拒绝生成新的 MXID。
当 MXID 用尽时,正常的运行恢复方式与 XID 用尽时非常相似。遵循上一节中的相同步骤,但有以下区别:
pg_stat_activity
)中;但是,查找旧 XID 仍然是确定哪些事务导致 MXID 回绕问题的好方法。PostgreSQL 有一个可选但强烈推荐的功能,称为自动清理,其目的是自动化 VACUUM
和 ANALYZE
命令的执行。启用后,自动清理会检查已插入、更新或删除大量元组的表。这些检查使用统计信息收集设施;因此,除非 track_counts 设置为 true
,否则无法使用自动清理。在默认配置中,自动清理是启用的,并且相关的配置参数设置正确。
“自动清理守护进程”实际上由多个进程组成。有一个持久的守护进程,称为自动清理启动器,它负责为所有数据库启动自动清理工作进程。启动器会将工作分配到一段时间内,并尝试每 autovacuum_naptime 秒在每个数据库中启动一个工作进程。(因此,如果安装中有N
个数据库,则每 autovacuum_naptime
/N
秒将启动一个新的工作进程。)最多允许 autovacuum_max_workers 个工作进程同时运行。如果需要处理的数据库多于 autovacuum_max_workers
个,则下一个数据库将在第一个工作进程完成后立即处理。每个工作进程将检查其数据库中的每个表,并在需要时执行 VACUUM
和/或 ANALYZE
。log_autovacuum_min_duration 可以设置为监视自动清理工作进程的活动。
如果多个大表在短时间内都符合清理条件,则所有自动清理工作进程可能都会长时间忙于清理这些表。这将导致其他表和数据库在工作进程可用之前无法被清理。在一个数据库中运行的工作进程数量没有限制,但工作进程会尽量避免重复其他工作进程已完成的工作。请注意,运行的工作进程数量不计入 max_connections 或 superuser_reserved_connections 的限制。
relfrozenxid
值比 autovacuum_freeze_max_age 事务旧的表总是会被清理(这也适用于那些通过存储参数修改了 freeze max age 的表;见下文)。否则,如果自上次 VACUUM
以来废弃的元组数量超过了“清理阈值”,则该表将被清理。清理阈值定义为:
vacuum threshold = Minimum(vacuum max threshold, vacuum base threshold + vacuum scale factor * number of tuples)
其中清理最大阈值是 autovacuum_vacuum_max_threshold,清理基本阈值是 autovacuum_vacuum_threshold,清理比例因子是 autovacuum_vacuum_scale_factor,元组数是 pg_class
.reltuples
。
如果自上次清理以来插入的元组数量超过了定义的插入阈值,则也会清理该表,该阈值定义为:
vacuum insert threshold = vacuum base insert threshold + vacuum insert scale factor * number of tuples
其中清理插入基本阈值是 autovacuum_vacuum_insert_threshold,清理插入比例因子是 autovacuum_vacuum_insert_scale_factor。此类清理可以允许表的部分被标记为所有可见,并允许元组被冻结,这可以减少后续清理所需的工作。对于接收 INSERT
操作但几乎不接收 UPDATE
/DELETE
操作的表,降低表的 autovacuum_freeze_min_age 可能会有益,因为这可以允许元组被更早的清理冻结。废弃元组的数量和插入元组的数量从累积统计系统获取;这是一个最终一致的计数,由每个 UPDATE
、DELETE
和 INSERT
操作更新。如果表的 relfrozenxid
值比 vacuum_freeze_table_age
事务旧,则会执行强制清理以冻结旧元组并推进 relfrozenxid
。
对于分析,使用类似的条件:阈值,定义为:
analyze threshold = analyze base threshold + analyze scale factor * number of tuples
与自上次 ANALYZE
以来插入、更新或删除的总元组数进行比较。
分区表不直接存储元组,因此不被自动清理处理。(自动清理会像其他表一样处理表分区。)不幸的是,这意味着自动清理不会对分区表运行 ANALYZE
,这可能导致引用分区表统计信息的查询计划不佳。您可以通过在分区表首次填充时手动运行 ANALYZE
,并在其分区中的数据分布发生显着变化时再次运行来解决此问题。
临时表无法被自动清理访问。因此,应通过会话 SQL 命令执行适当的清理和分析操作。
默认阈值和比例因子取自 postgresql.conf
,但可以为每个表覆盖它们(以及许多其他自动清理控制参数);有关更多信息,请参阅存储参数。如果某个设置已通过表的存储参数更改,则处理该表时将使用该值;否则将使用全局设置。有关全局设置的更多详细信息,请参阅第 19.10.1 节。
当多个工作进程运行时,自动清理成本延迟参数(请参阅第 19.10.2 节)会在所有运行的工作进程之间进行“平衡”,以便系统的总 I/O 影响与实际运行的工作进程数量无关。但是,处理具有已设置的每个表 autovacuum_vacuum_cost_delay
或 autovacuum_vacuum_cost_limit
存储参数的表的任何工作进程都不会在平衡算法中被考虑。
自动清理工作进程通常不会阻止其他命令。如果一个进程尝试获取与自动清理持有的 SHARE UPDATE EXCLUSIVE
锁冲突的锁,锁获取将中断自动清理。有关冲突的锁模式,请参阅表 13.2。但是,如果自动清理是为了防止事务 ID 回绕(即 pg_stat_activity
视图中的自动清理查询名称以 (to prevent wraparound)
结尾),则自动清理不会自动中断。
定期运行与 SHARE UPDATE EXCLUSIVE
锁冲突的命令(例如 ANALYZE)可能会有效地阻止自动清理完成。
如果您在文档中看到任何不正确之处、与您对特定功能的体验不符之处或需要进一步澄清之处,请使用此表格报告文档问题。