PostgreSQL 数据库需要称为清理的定期维护。对于许多安装,让自动清理守护进程执行清理就足够了,该进程在第 24.1.6 节中描述。您可能需要调整那里描述的自动清理参数,以便为您的具体情况获得最佳结果。一些数据库管理员希望通过手动管理的 VACUUM
命令来补充或替换守护进程的活动,这些命令通常按照 cron 或 任务计划程序 脚本的时间表执行。要正确设置手动管理的清理,必须理解以下几个小节中讨论的问题。依赖自动清理的管理员仍然可能希望浏览这些材料,以帮助他们理解和调整自动清理。
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.4.4 节。
在 PostgreSQL 中,行的 UPDATE
或 DELETE
不会立即删除该行的旧版本。这种方法对于获得多版本并发控制 (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
,因此它们对于所有正常事务都将显示为“在过去”,而不管回绕问题,因此此类行版本将有效直至删除,无论时间多长。
在 9.4 之前的 PostgreSQL 版本中,冻结是通过实际将行的插入 XID 替换为 FrozenTransactionId
来实现的,这在行的 xmin
系统列中可见。较新的版本仅设置一个标志位,保留行的原始 xmin
以供可能的取证使用。但是,在从 9.4 之前的版本 pg_upgrade' 的数据库中仍然可以找到 xmin
等于 FrozenTransactionId
(2) 的行。
此外,系统目录可能包含 xmin
等于 BootstrapTransactionId
(1) 的行,这表明它们是在 initdb 的第一阶段插入的。与 FrozenTransactionId
类似,此特殊 XID 被视为比每个正常的 XID 都旧。
vacuum_freeze_min_age 控制一个 XID 值必须有多旧,才能将带有该 XID 的行冻结。如果将要被冻结的行很快将被再次修改,则增加此设置可能会避免不必要的工作,但降低此设置会增加表必须再次被清理之前可以经过的事务数量。
VACUUM
使用可见性映射来确定必须扫描表的哪些页。通常,它将跳过没有任何已删除行版本的页面,即使这些页面可能仍然具有具有旧 XID 值的行版本。因此,正常的 VACUUM
不会总是冻结表中的每个旧行版本。发生这种情况时,VACUUM
最终需要执行一个积极的清理,这将冻结所有符合条件但未冻结的 XID 和 MXID 值,包括那些来自所有可见但并非所有冻结页面的值。在实践中,大多数表都需要定期进行积极的清理。vacuum_freeze_table_age 控制 VACUUM
何时执行此操作:如果自上次此类扫描以来经过的事务数量大于 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_freeze_max_age
减去 vacuum_freeze_min_age
个事务被调用一次。对于定期清理以进行空间回收的表,这无关紧要。然而,对于静态表(包括接收插入但不接收更新或删除的表),无需清理以进行空间回收,因此尝试最大化在非常大的静态表上强制进行自动清理的时间间隔可能很有用。显然,可以通过增加 autovacuum_freeze_max_age
或减少 vacuum_freeze_min_age
来做到这一点。
vacuum_freeze_table_age
的有效最大值为 0.95 * autovacuum_freeze_max_age
;高于此值的设置将被限制为最大值。高于 autovacuum_freeze_max_age
的值没有意义,因为此时无论如何都会触发反回卷的自动清理,而 0.95 的乘数则留出了一些缓冲空间,以便在此之前运行手动 VACUUM
。通常来说,vacuum_freeze_table_age
应该设置为略低于 autovacuum_freeze_max_age
的值,留出足够的间隔,以便在此窗口中运行定期计划的 VACUUM
或由正常的删除和更新活动触发的自动清理。设置得太接近可能会导致反回卷的自动清理,即使表最近被清理以回收空间,而较低的值会导致更频繁的激进清理。
增加 autovacuum_freeze_max_age
(以及随之增加的 vacuum_freeze_table_age
)的唯一缺点是,数据库集群的 pg_xact
和 pg_commit_ts
子目录将占用更多空间,因为它必须存储所有事务回溯到 autovacuum_freeze_max_age
水平线的提交状态和(如果启用了 track_commit_timestamp
)时间戳。提交状态每个事务使用 2 位,因此如果将 autovacuum_freeze_max_age
设置为允许的最大值 20 亿,则 pg_xact
预计会增长到大约 0.5 GB,而 pg_commit_ts
会增长到大约 20 GB。如果这与您的总数据库大小相比微不足道,则建议将 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
结束时剩余的最旧的未冻结 XID(通常是最近的激进 VACUUM)。类似地,数据库的 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
通常只扫描自上次清理以来已修改的页面,但是只有在扫描了表中可能包含未冻结 XID 的每个页面时,才能推进 relfrozenxid
。当 relfrozenxid
的事务年龄超过 vacuum_freeze_table_age
时,当使用 VACUUM
的 FREEZE
选项时,或者当所有未完全冻结的页面恰好都需要清理以删除已失效的行版本时,就会发生这种情况。当 VACUUM
扫描表中所有尚未完全冻结的页面时,它应该将 age(relfrozenxid)
设置为略高于所使用的 vacuum_freeze_min_age
设置的值(由于自 VACUUM
开始以来启动的事务数量而更高)。VACUUM
会将 relfrozenxid
设置为表中保留的最旧的 XID,因此最终值可能比严格要求的要新得多。如果在达到 autovacuum_freeze_max_age
之前没有对表发出任何推进 relfrozenxid
的 VACUUM
,则很快会强制对该表进行自动清理。
如果由于某种原因,自动清理未能从表中清除旧的 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
。)如果忽略这些警告,则一旦剩余的事务数少于 300 万,系统将拒绝分配新的 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
它们。存在 300 万个事务的安全边际,以便管理员执行此操作。有关使用单用户模式的详细信息,请参见 postgres 参考页。
多事务 ID 用于支持多个事务的行锁定。由于元组标头中用于存储锁定信息的空间有限,因此当多个事务同时锁定一行时,该信息被编码为“多个事务 ID”,或简称多事务 ID。有关任何特定多事务 ID 中包含哪些事务 ID 的信息单独存储在 pg_multixact
子目录中,并且只有多事务 ID 出现在元组标头的 xmax
字段中。与事务 ID 一样,多事务 ID 也实现为 32 位计数器和相应的存储,所有这些都需要仔细的老化管理、存储清理和回卷处理。有一个单独的存储区域,其中保存每个多事务的成员列表,该区域也使用 32 位计数器,并且也必须进行管理。
每当 VACUUM
扫描表的任何部分时,它都会将遇到的任何比 vacuum_multixact_freeze_min_age 旧的多事务 ID 替换为不同的值,该值可以是零值、单个事务 ID 或较新的多事务 ID。对于每个表,pg_class
.relminmxid
存储该表的任何元组中仍出现的最旧可能的多事务 ID。如果此值比 vacuum_multixact_freeze_table_age 旧,则会强制进行激进清理。如上一节所述,激进清理意味着只会跳过已知完全冻结的页面。可以在 pg_class
.relminmxid
上使用 mxid_age()
来查找其年龄。
无论是什么原因导致激进的 VACUUM
,都可以 保证 能够推进表的 relminmxid
。最终,随着所有数据库中的所有表都被扫描并且它们最旧的多事务值被推进,可以删除旧多事务的磁盘存储。
作为一种安全措施,对于任何多事务年龄大于 autovacuum_multixact_freeze_max_age 的表,将发生激进的清理扫描。此外,如果多事务成员占用的存储空间超过 2GB,则会对所有表更频繁地进行激进的清理扫描,从多事务年龄最旧的表开始。即使名义上禁用了自动清理,也会发生这两种类型的激进扫描。
与 XID 情况类似,如果自动清理未能从表中清除旧的 MXID,则当数据库中最旧的 MXID 从回卷点达到 4000 万个事务时,系统将开始发出警告消息。并且,就像 XID 情况一样,如果忽略这些警告,则一旦剩余的事务数少于 300 万,系统将拒绝生成新的 MXID,直到回卷。
当 MXID 耗尽时,可以像 XID 耗尽时一样恢复正常运行。按照上一节中的相同步骤操作,但有以下区别
pg_stat_activity
的系统视图中;然而,查找旧的 XID 仍然是确定哪些事务导致 MXID 回卷问题的好方法。PostgreSQL 有一个可选但强烈推荐的功能叫做 autovacuum(自动清理),其目的是自动化执行 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 的事务时间还旧,则始终会清理该表(这也适用于那些通过存储参数修改了冻结最大期限的表;见下文)。否则,如果自上次 VACUUM
以来被废弃的元组数量超过了 “清理阈值”,则会清理该表。清理阈值的定义为
vacuum threshold = vacuum base threshold + vacuum scale factor * number of tuples
其中清理基础阈值为 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
,并在其分区中的数据分布发生显着变化时再次运行 ANALYZE
来解决此问题。
自动清理无法访问临时表。因此,应通过会话 SQL 命令执行适当的清理和分析操作。
默认阈值和比例因子取自 postgresql.conf
,但可以在每个表的基础上覆盖它们(以及许多其他自动清理控制参数);有关更多信息,请参阅存储参数。如果通过表的存储参数更改了设置,则在处理该表时使用该值;否则,使用全局设置。有关全局设置的更多详细信息,请参阅第 19.10 节。
当多个工作进程运行时,自动清理成本延迟参数(请参阅第 19.4.4 节)在所有正在运行的工作进程之间进行 “平衡”,以便对系统的总 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)可能会有效地阻止自动清理完成。
如果您在文档中看到任何不正确、与您特定功能体验不符或需要进一步澄清的内容,请使用此表格报告文档问题。