9.3 9.4 9.5 9.6 10 11 12 13 14 Current(15)
阿里云PostgreSQL 问题报告 纠错本页面

25.1. 日常清理

25.1.1. 清理的基础知识
25.1.2. 恢复磁盘空间
25.1.3. 更新规划器统计信息
25.1.4. 更新可见性映射
25.1.5. 防止事务 ID 回卷失败
25.1.6. 自动清理后台进程

PostgreSQL数据库要求周期性的清理维护。对于很多安装,让自动清理守护进程来执行清理已经足够,如第 25.1.6 节所述。你可能需要调整其中描述的自动清理参数来获得最佳结果。某些数据库管理员会希望使用手动管理的VACUUM命令来对后台进程的活动进行补充或者替换,这通常使用cron任务计划程序脚本来执行。要正确地设置手动管理的清理,最重要的是理解接下来几小节中讨论的问题。依赖自动清理的管理员最好也能略读该内容以帮助他们理解和调整自动清理。

25.1.1. 清理的基础知识

PostgreSQLVACUUM命令出于几个原因必须定期处理每一个表:

  1. 恢复或重用被已更新或已删除行所占用的磁盘空间。
  2. 更新被PostgreSQL查询规划器使用的数据统计信息。
  3. 更新可见性映射,它可以加速只用索引的扫描。
  4. 保护老旧数据不会由于事务ID回卷多事务ID回卷而丢失。

正如后续小节中解释的,每一个原因都将指示以不同的频率和范围执行VACUUM操作。

有两种VACUUM的变体:标准VACUUMVACUUM FULLVACUUM FULL可以收回更多磁盘空间但是运行起来更慢。另外,标准形式的VACUUM可以和生产数据库操作并行运行(SELECTINSERTUPDATEDELETE等命令将继续正常工作,但在清理期间你无法使用ALTER TABLE等命令来更新表的定义)。VACUUM FULL要求在其工作的表上得到一个ACCESS EXCLUSIVE 锁,因此无法和对此表的其他使用并行。因此,通常管理员应该努力使用标准VACUUM并且避免VACUUM FULL

VACUUM会产生大量I/O流量,这将导致其他活动会话性能变差。可以调整一些配置参数来后台清理活动造成的性能冲击 — 参阅第 20.4.4 节

25.1.2. 恢复磁盘空间

PostgreSQL中,一次行的UPDATEDELETE不会立即移除该行的旧版本。这种方法对于从多版本并发控制(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再用VACUUMTRUNCATE会立刻移除该表的整个内容,而不需要一次后续的VACUUMVACUUM FULL来回收现在未被使用的磁盘空间。其缺点是会违背严格的 MVCC 语义。

25.1.3. 更新规划器统计信息

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以保持统计信息最新。

25.1.4. 更新可见性映射

清理机制为每一个表维护着一个可见性映射,它被用来跟踪哪些页面只包含对所有活动事务(以及所有未来的事务,直到该页面被再次修改)可见的元组。这样做有两个目的。第一,清理本身可以在下一次运行时跳过这样的页面,因为其中没有什么需要被清除。

第二,这允许PostgreSQL回答一些只用索引的查询,而不需要引用底层表。因为PostgreSQL的索引不包含元组的可见性信息,一次普通的索引扫描会为每一个匹配的索引项获取堆元组,用来检查它是否能被当前事务所见。另一方面,一次只用索引的扫描会首先检查可见性映射。如果它了解到在该页面上的所有元组都是可见的,堆获取就可以被跳过。这对大数据集很有用,因为可见性映射可以防止磁盘访问。可见性映射比堆小很多,因此即使堆非常大,可见性映射也可以很容易地被缓存起来。

25.1.5. 防止事务 ID 回卷失败

PostgreSQL的 MVCC 事务语义依赖于能够比较事务 ID(XID)数字:如果一个行版本的插入 XID 大于当前事务的 XID,它就是属于未来的并且不应该对当前事务可见。但是因为事务 ID 的尺寸有限(32位),一个长时间(超过 40 亿个事务)运行的集簇会遭受到事务 ID 回卷问题:XID 计数器回卷到 0,并且本来属于过去的事务突然间就变成了属于未来 — 这意味着它们的输出变成不可见。简而言之,灾难性的数据丢失(实际上数据仍然在那里,但是如果你不能得到它也无济于事)。为了避免发生这种情况,有必要至少每 20 亿个事务就清理每个数据库中的每个表。

周期性的清理能够解决该问题的原因是,VACUUM会把行标记为 冻结,这表示它们是被一个在足够远的过去提交的事务所插入, 这样从 MVCC 的角度来看,效果就是该插入事务对所有当前和未来事务来说当然都 是可见的。PostgreSQL保留了一个特殊的 XID (FrozenTransactionId),这个 XID 并不遵循普通 XID 的比较规则 并且总是被认为比任何普通 XID 要老。普通 XID 使用模-232算 法来比较。这意味着对于每一个普通 XID都有 20 亿个 XID 更老并且 有 20 亿个更新,另一种解释的方法是普通 XID 空间是没有端点的环。 因此,一旦一个行版本创建时被分配了一个特定的普通 XID,该行版本将成为接下 来 20 亿个事务的过去(与我们谈论的具体哪个普通 XID 无关)。如 果在 20 亿个事务之后该行版本仍然存在,它将突然变得好像在未来。要阻止这一切 发生,被冻结行版本会被看成其插入 XID 为FrozenTransactionId, 这样它们对所有普通事务来说都是在过去,而不管回卷问题。并且这样 的行版本将一直有效直到被删除,不管它有多旧。

注意

在9.4之前的PostgreSQL版本中,实际上会通过将一行的插入 XID 替换为 FrozenTransactionId来实现冻结,这种FrozenTransactionId在行的 xmin系统列中是可见的。较新的版本只是设置一个标志位, 保留行的原始xmin用于可能发生的鉴别用途。不过, 在9.4之前版本的数据库pg_upgrade中可能仍会找到 xmin等于FrozenTransactionId (2)的行。

此外,系统目录可能会包含xmin等于BootstrapTransactionId (1) 的行,这表示它们是在initdb的第一个阶段被插入的。 和FrozenTransactionId相似,这个特殊的 XID 被认为比所有正常 XID 的年龄都要老。

vacuum_freeze_min_age控制在其行版本被冻结前一个 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上次扫描全表时的vacuum_freeze_min_age值。如果它超过该时间没有被清理,可能会导致数据丢失。要保证这不会发生,将在任何包含比autovacuum_freeze_max_age配置参数所指定的年龄更老的 XID 的未冻结行的表上调用自动清理(即使自动清理被禁用也会发生)。

这意味着如果一个表没有被清理,大约每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_xactpg_commit_ts子目录将占据更多空间,因为它必须存储所有向后autovacuum_freeze_max_age范围内的所有事务的提交状态和(如果启用了track_commit_timestamp)时间戳。提交状态为每个事务使用两个二进制位,因此如果autovacuum_freeze_max_age被设置为它的最大允许值 20 亿,pg_xact将会增长到大约 0.5 吉字节,pg_commit_ts大约20GB。如果这对于你的总数据库尺寸是微小的,我们推荐设置autovacuum_freeze_max_age为它的最大允许值。否则,基于你想要允许pg_xactpg_commit_ts使用的存储空间大小来设置它(默认情况下 2 亿个事务大约等于pg_xact的 50 MB存储空间,pg_commit_ts的2GB的存储空间)。

减小vacuum_freeze_min_age的一个不足之处是它可能导致VACUUM做无用的工作:如果该行在被替换成FrozenXID之后很快就被修改(导致该行获得一个新的 XID),那么冻结一个行版本就是浪费时间。因此该设置应该足够大,这样直到行不再可能被修改之前,它们都不会被冻结。

要跟踪数据库中最老的未冻结XID的年龄,VACUUM将XID统计信息存储在系统表 pg_classpg_database中。特别地, 表的pg_class行的relfrozenxid列包含 在最近一次成功推进relfrozenxid(通常是最近的主动VACUUM)结束时 的最老未冻结XID。同样,数据库的pg_database行的 datfrozenxid列是该数据库中出现的未冻结XIDs的下界 —— 它只是 数据库中每个表的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会打印关于表的各种统计信息。这包括关于 relfrozenxidrelminmxid 如何增加的信息。当自动清理日志(由log_autovacuum_min_duration 控制)报告由自动清理执行的VACUUM操作时,相同的详细信息会出现在服务器日志中。

VACUUM通常只扫描自上次清理以来已经修改的页面,但relfrozenxid只能在扫描可能包含未冻结XID的表的每个页面时才能推进。 当relfrozenxid超过vacuum_freeze_table_age个事务时,当使用VACUUMFREEZE选项时,或者当所有未全部冻结的页面碰巧需要进行清理以删除死行版本时,会发生这种情况。 当VACUUM扫描表中尚未全部冻结的每个页面时,应将age(relfrozenxid)设置为比使用的vacuum_freeze_min_age设置稍高一点的值(再加上自VACUUM开始以来启动的事务数)。 VACUUMrelfrozenxid设置为表中仍存在的最旧XID,因此最终值可能比严格要求的要新得多。 如果在达到autovacuum_freeze_max_age之前没有对表发出推进relfrozenxidVACUUM,则很快将为该表强制执行自动清理。

如果由于某种原因,自动清理程序未能清除表中的旧XID,当数据库的最旧XID达到距离回绕点四千万次事务时,系统将开始发出如下警告消息:

WARNING:  database "mydb" must be vacuumed within 39985967 transactions
HINT:  To avoid a database shutdown, execute a database-wide VACUUM in that database.

(如提示所建议,手动执行VACUUM应该可以解决问题;但请注意,VACUUM应该由超级用户执行,否则将无法处理系统目录,从而无法推进数据库的datfrozenxid。) 如果忽略这些警告,系统将在距离回绕点不足三百万次事务时拒绝分配新的XID:

ERROR:  database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT:  Stop the postmaster and vacuum that database in single-user mode.

在这种情况下,已经在进行的任何事务可以继续,但只能启动只读事务。修改数据库记录或截断关系的操作将失败。 VACUUM命令仍然可以正常运行。 与提示所述相反,为了恢复正常操作,无需停止postmaster或进入单用户模式。 取而代之,请按照以下步骤操作:

  1. 解决旧的预备事务。您可以通过pg_prepared_xacts 检查age(transactionid)较大的行来查找这些事务,可以在其中找到这些事务,这些事务应该被提交或回滚。
  2. 结束长时间运行的开放事务。您可以通过pg_stat_activity 检查age(backend_xid)age(backend_xmin)较大的行来查找这些事务,这些事务应该被提交或回滚, 或者可以使用pg_terminate_backend终止会话。
  3. 删除任何旧的复制插槽。使用pg_stat_replication检查age(xmin)age(catalog_xmin)较大的插槽,可以找到这些插槽。在许多情况下,这些插槽是为了复制到已不存在或长时间关闭的服务器而创建的。如果删除了仍然存在并可能尝试连接到该插槽的服务器的插槽,则可能需要重建该副本。
  4. 在目标数据库中执行VACUUM。对整个数据库执行VACUUM是最简单的方法;为了减少所需的时间,也可以在relminxid最老的表上发出手动VACUUM命令。在这种情况下不要使用VACUUM FULL,因为它需要一个XID,因此会失败,除非在超级用户模式下,在那里它将消耗一个XID,从而增加事务ID回绕的风险。也不要使用VACUUM FREEZE,因为它将执行比恢复正常操作所需的最小工作量更多的工作。
  5. 一旦恢复正常操作,请确保在目标数据库中正确配置自动清理程序,以避免未来出现问题。

注意

在早期版本中,有时需要停止postmaster并以单用户模式VACUUM数据库。 在典型情况下,这不再是必要的,应尽量避免,因为这会导致系统停机。 这也更加危险,因为它会禁用旨在防止数据丢失的事务ID环绕保护措施。 在这种情况下使用单用户模式的唯一原因是,如果您希望TRUNCATEDROP不需要的表以避免需要VACUUM它们。 三百万事务的安全边界存在是为了让管理员执行此操作。有关使用单用户模式的详细信息,请参阅postgres参考页面。

25.1.5.1. 多事务和回卷

Multixact 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。 最终,当所有数据库中的所有表都被扫描并且它们最老的多重事务值被推进时,旧的多重事务的磁盘存储可以被移除。

作为一种安全设备,对于任何多事务ID的年龄大于autovacuum_multixact_freeze_max_age的表, 将进行积极的真空扫描。此外,如果多事务ID成员占用的存储空间超过2GB, 则对所有表更频繁地进行积极的真空扫描,从具有最老的多事务ID年龄的表开始。 即使自动真空名义上被禁用,这两种类型的积极扫描也会发生。

与XID案例类似,如果自动清理程序无法清除表中的旧MXID,当数据库的最旧MXID达到距离 回绕点四千万个事务时,系统将开始发出警告消息。而且,就像XID案例一样,如果忽略 这些警告,一旦距离回绕点不足三百万个事务,系统将拒绝生成新的MXID。

当MXIDs用尽时,可以通过与XIDs用尽时相同的方式恢复正常操作。按照前一节中的相同步骤进行, 但有以下区别:

  1. 如果没有可能出现在多重事务中的事务和准备事务,则可以忽略运行事务和准备事务。
  2. MXID信息在诸如pg_stat_activity之类的系统视图中不直接可见; 但是,查找旧的XIDs仍然是确定哪些事务导致MXID环绕问题的好方法。
  3. XID用尽将阻止所有写事务,但MXID用尽只会阻止一部分写事务,具体来说,那些涉及 需要MXID的行锁的写事务。

25.1.6. 自动清理后台进程

PostgreSQL有一个可选的但是被高度推荐的特性autovacuum,它的目的是自动执行VACUUMANALYZE命令。当它被启用时,自动清理会检查被大量插入、更新或删除元组的表。这些检查会利用统计信息收集功能,因此除非track_counts被设置为true,自动清理不能被使用。在默认配置下,自动清理是被启用的并且相关配置参数已被正确配置。

自动清理后台进程实际上由多个进程组成。有一个称为 自动清理启动器的常驻后台进程, 它负责为所有数据库启动自动清理工作者进程。 启动器将把工作散布在一段时间上,它每隔 autovacuum_naptime秒尝试在每个数据库中启动一个工作者 (因此,如果安装中有N个数据库,则每 autovacuum_naptime/N秒将启动一个新的工作者)。 在同一时间只允许最多autovacuum_max_workers 个工作者进程运行。如果有超过autovacuum_max_workers 个数据库需要被处理,下一个数据库将在第一个工作者结束后马上被处理。 每一个工作者进程将检查其数据库中的每一个表并且在需要时执行 VACUUM和/或ANALYZE。 可以设置log_autovacuum_min_duration 来监控自动清理工作者的活动。

如果在一小段时间内多个大型表都变得可以被清理,所有的自动清理工作者可能都会被占用来在一段长的时间内清理这些表。这将会造成其他的表和数据库无法被清理,直到一个工作者变得可用。对于一个数据库中的工作者数量并没有限制,但是工作者确实会试图避免重复已经被其他工作者完成的工作。注意运行着的工作者的数量不会被计入max_connectionssuperuser_reserved_connections限制。

relfrozenxid值比autovacuum_freeze_max_age事务年龄更大的表总是会被清理(这页表示这些表的冻结最大年龄被通过表的存储参数修改过,参见后文)。否则,如果从上次VACUUM以来失效的元组数超过清理阈值,表也会被清理。清理阈值定义为:

清理阈值 = 清理基本阈值 + 清理缩放系数 * 元组数

其中清理基本阈值为autovacuum_vacuum_threshold, 清理缩放系数为autovacuum_vacuum_scale_factor, 元组数为pg_class.reltuples

如果自上次清理以来插入的元组数量超过定义的插入阈值,则还会对表进行清理,该阈值定义为:

清理插入阈值 = 清理基本插入阈值 + 清理插入比例因子 * 元组数量

其中清理插入基本阈值是 autovacuum_vacuum_insert_threshold, 而清理插入比例因子是 autovacuum_vacuum_insert_scale_factor. 这样的清理可能会将表的部分标记为 全部可见,并且还可以冻结元组,这样可以减少后续清理所需的工作量。 对于接收INSERT操作但几乎没有或几乎没有UPDATE/DELETE操作的表, 降低表的 autovacuum_freeze_min_age可能是有益的,因为这可能允许早期的清理冻结元组。 废弃元组的数量和插入元组的数量是从累积统计系统中获取的; 这是一个由每个UPDATEDELETEINSERT操作更新的半准确计数。 (它只是半准确的,因为在重载下可能会丢失一些信息。) 如果表的relfrozenxid值比vacuum_freeze_table_age个事务更旧, 则执行积极的清理以冻结旧元组并推进 relfrozenxid;否则,只扫描自上次清理以来已修改的页面。

对于分析,也使用了一个相似的阈值:

分析阈值 = 分析基本阈值 + 分析缩放系数 * 元组数

该阈值将与自从上次ANALYZE以来被插入、更新或删除的元组数进行比较。

分区表不直接存储元组,因此不会被自动清理处理。(自动清理会像处理其他表一样处理表分区。)不幸的是,这意味着自动清理不会在分区表上运行ANALYZE,这可能导致引用分区表统计信息的查询产生次优化的计划。当分区表首次填充时,您可以通过手动运行ANALYZE来解决这个问题,并在它们的分区数据分布发生显著变化时再次运行。

临时表不能被自动清理访问。因此,临时表的清理和分析操作必须通过会话期间的SQL命令来执行。

默认的阈值和缩放系数都取自于postgresql.conf,但是可以为每一个表重写它们(和许多其他自动清理控制参数), 详情参见Storage Parameters。 如果一个设置已经通过一个表的存储参数修改,那么在处理该表时使用该值,否则使用全局设置。 全局设置请参阅第 20.10 节

当多个工作者运行时,在所有运行着的工作者之间自动清理代价延迟参数 (参阅第 20.4.4 节)是 平衡的,这样不管实际运行的工作者数量是多少, 对于系统的总体 I/O 影响总是相同的。不过,任何正在处理已经设置了每表 autovacuum_vacuum_cost_delayautovacuum_vacuum_cost_limit 存储参数的表的工作者不会被考虑在均衡算法中。

autovacuum工作进程通常不会阻止其他命令。如果某个进程尝试获取与autovacuum持有的SHARE UPDATE EXCLUSIVE锁冲突的锁,则锁获取将中断该autovacuum。有关冲突的锁定模式,请参见表 13.2。 但是,如果autovacuum正在运行以防止事务ID回卷(即在pg_stat_activity视图中的autovacuum查询名以(to prevent wraparound)结尾),则autovacuum不会被自动中断。

警告

定期运行需要获取与SHARE UPDATE EXCLUSIVE锁冲突的锁的命令(例如ANALYZE)可能会让autovacuum始终无法完成。