PolarDB集群需要进行周期性维护清理。通常情况下,使用自动清理守护进程处理即可。您可以根据业务需求调整自动清理参数来优化处理效果。同时,支持使用VACUUM命令手动管理后台进程,该命令通常使用cron
或任务计划程序脚本来执行。
背景
因存在以下问题,PolarDB集群需要进行周期性执行VACUUM操作。
回收被更新或删除行占用的磁盘空间。
更新PolarDB查询规划器使用的数据统计信息。
更新可见性映射,可加速仅索引扫描。
保护老旧数据,避免因为事务ID回卷或多事务ID回卷导致的数据丢失。
以上各个问题要求PolarDB集群以不同频率和范围执行VACUUM操作。VACUUM包括以下两类:
标准VACUUM:支持与数据库操作并行。
说明支持并行的数据库操作包括SELECT、INSERT、UPDATE和DELETE等命令,不支持在清理期间使用ALTER TABLE更新表定义。
VACUUM FULL:对比标准VACUUM,支持回收更多磁盘空间,但运行速度更慢。同时,VACUUM FULL要求在其工作的表上得到一个排他锁,无法和对此表的其他数据库操作并行。
为保证集群运行连续性,建议优先使用标准VACUUM。此外,VACUUM操作会产生大量I/O流量影响其他活动会话性能
恢复磁盘空间
PolarDB PostgreSQL轻量版中一次性的UPDATE或DELETE操作不会立即移除该行的旧版本,这种设计是为了多版本并发控制:当旧版本仍可能对其他事务可见时,它不能被删除。但是,最终这个过时的或已删除的行版本不再对任何事务有用,它所占用的空间必须被回收,以便供新行重复使用,从而避免磁盘空间需求的无限制增长。这将通过VACUUM完成清理工作。
标准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的表重写变体之一。这些命令会重新写入整个表的副本,并为其构建新索引。所有这些选项都需要排他锁。同时,需要注意的是,直到新表和索引完成之前旧表和索引都不能被释放,因此将临时占用大约等于该表尺寸的额外磁盘空间。
如果您有一个周期性全量删除内容的表,建议使用TRUNCATE代替使用DELETE后再进行VACUUM。TRUNCATE可以立即删除表的全部内容,而无需后续的VACUUM或VACUUM FULL来回收现在未被使用的磁盘空间。但需要注意其对多版本并发控制(MVCC)的影响。
自动清理守护进程
PolarDB 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以来失效的元组数超过清理阈值,表也会被清理。清理阈值定义为:
其中,清理基本阈值为autovacuum_vacuum_threshold
, 清理缩放系数为autovacuum_vacuum_scale_factor
,元组数为pg_class.reltuples
。 失效元组数量可通过统计信息收集器获得,它是一个由每个UPDATE和DELETE命令更新的半准确的计数(只是半准确,因为在高负载的情况下某些信息可能丢失)。如果表的relfrozenxid
值大于vacuum_freeze_table_age
事务年龄,整个表将被扫描以冻结旧元组并增长relfrozenxid
,否则只有从上次清理以来被修改的页面会被扫描。
对于ANALYZE,也使用了一个相似的阈值:
该阈值将与自上次ANALYZE以来被插入、更新或删除的元组数进行比较。
临时表不能被自动清理访问。因此,必须通过会话期间的SQL命令进行临时表的清理和分析操作。
默认的阈值和缩放系数都来自集群配置文件postgresql.conf,但是可以在每个表的基础上重写相关参数。如果通过表的存储参数修改了某个配置,那么在处理该表时使用该值,否则使用全局设置。
当多个工作进程运行时,在所有进程之间自动清理代价延迟参数是“平衡的”,以确保对系统的总I/O影响保持不变。但是,任何正在处理表的工作进程,如果已经设置了每个表的autovacuum_vacuum_cost_delay
或autovacuum_vacuum_cost_limit
存储参数,该进程将不会被考虑在均衡算法中。
日常重建索引
在某些场景下,需要通过周期性执行REINDEX命令或采用多步骤独立重构来重建索引,以维护索引性能。首先,对于已完全空闲的B树索引页面回收重用,可直接使用周期性索引重建。当B树索引页面的键值被大量删除,系统会回收完全空闲的页面,但部分填充的页面仍可能导致空间浪费。推荐使用定期重建索引。此外,使用新建的B树索引查询效率比更新多次的索引略快,这是由于在新建索引上,逻辑相邻的页面通常物理也相邻(这样的考虑目前并不适用于非B树索引)。对此,仅为提高访问速度也建议您定期重建索引。
对于非B树索引可能存在的膨胀尚未形成很好地定量分析。因此,建议在使用非B树索引时增加索引物理尺寸的定期监控。
对于重建索引操作,在所有情况下REINDEX都可以安全和容易地使用,但是该命令要求一个排他锁。推荐使用一个由创建和替换步骤组成的序列执行索引重建。可以先使用CREATE INDEX CONCURRENTLY创建索引,如果创建成功并且该索引可用,则原来的索引可以使用ALTER INDEX和DROP INDEX的命令组合替换成新创建的索引。当索引涉及强制唯一性或者其他约束时,可能需要使用ALTER TABLE将现有的约束更新为新索引所强制的约束。在使用多步骤重建方法之前应仔细检查,因为对于哪些索引可以采用多步骤重建索引存在限制,一旦出现的错误需及时处理。