如何排查 autovaccum 在 Amazon RDS for PostgreSQL 或 Aurora PostgreSQL 兼容版数据库中触发 "to prevent wraparound"(“防止事务回卷”)标志的问题?
我想解决 autovaccum 在 Amazon Relational Database Service (Amazon RDS) for PostgreSQL 或 Amazon Aurora PostgreSQL 兼容版数据库中触发 "to prevent wraparound"(“防止事务回卷”)标志时的性能问题。
简短描述
当您查询 pg_stat_activity 时,autovacuum 可能会持续运行并触发 "to prevent wraparound"(“防止事务回卷”)标志。
autovacuum worker 通常不会阻止其他命令。当某个进程尝试获取与 autocvacuum 的 SHARE UPDATE EXCLUSIVE 锁相冲突的锁时,系统可能会中断 autovacuum。但是,当 autovacuum 为防止事务 ID 回卷而运行时,系统不会自动中断该进程。
在以下场景中,可能会触发带有 "to prevent wraparound"(“防止事务回卷”)标志的激进 autovacuum:
- 表的 relfrozenxid 值超过了 vacuum_freeze_table_age 事务数,因此系统执行激进 vacuum 来冻结旧元组并推进 relfrozenxid。
- 表达到了其 autovacuum_freeze_max_age 限制。
当 autovacuum 激进运行时,可能会出现数据库性能问题。有关更多信息,请参阅 PostgreSQL 网站上的 Automatic vacuuming。
解决方法
确定 autovacuum 是否正在运行
要确定 autovacuum 是否正在运行、运行多长时间或是否正在等待,请运行以下查询:
SELECT datname, usename, pid, state, wait_event, current_timestamp - xact_start AS xact_runtime, query FROM pg_stat_activity WHERE upper(query) LIKE '%VACUUM%' ORDER BY xact_start;
在以下输出示例中,带有 "to prevent wraparound"(“防止事务回卷”)标志的进程正在 mydb 的 mytable1 和 mytable2 上运行:
datname | usename | pid | state | wait_event | xact_runtime | query --------+----------+-------+--------+------------+-------------------------+---------------------------------------------------------------------------------------------- mydb | rdsadmin | 16473 | active | | 33 days 16:32:11.600656 | autovacuum: VACUUM ANALYZE public.mytable1 (to prevent wraparound) mydb | rdsadmin | 22553 | active | | 14 days 09:15:34.073141 | autovacuum: VACUUM ANALYZE public.mytable2 (to prevent wraparound) mydb | rdsadmin | 41909 | active | | 3 days 02:43:54.203349 | autovacuum: VACUUM ANALYZE public.mytable3 mydb | rdsadmin | 618 | active | | 00:00:00 | SELECT datname, usename, pid, state, wait_event, current_timestamp - xact_start AS xact_runtime, query+ | | | | | | FROM pg_stat_activity + | | | | | | WHERE query like '%VACUUM%'+ | | | | | | ORDER BY xact_start; +
若表的 relfrozenxid 超过 autovaccum_freeze_max_age 中的事务数量,autovaccum 就会锁定这些表。
识别未冻结的事务
要识别数据库中未冻结的事务,请运行以下查询:
SELECT datname, age(datfrozenxid) FROM pg_database ORDER BY age(datfrozenxid) desc limit 20;
在以下输出示例中,autovacuum 守护进程会优先处理 datfrozenxid 超过 2 亿的数据库:
datname | age mydb | 1771757888 template0 | 1721757888 template1 | 1721757888 rdsadmin | 1694008527 postgres | 1693881061 (5 rows)
数据库的 pg_database 行的 datfrozenxid 列位于数据库中正常事务 ID 的末尾。datfrozenxid 列是数据库内各表 relfrozenxid 的最小值。
冻结表
如果已达到 relfrozenxid 最大表年龄,请手动对表执行 vacuum 冻结操作。
手动执行 vacuum 冻结操作完成后,查看详细输出以确定是否存在阻止程序,例如旧复制槽和已准备事务。如果您收到 "DETAIL: xxx dead row versions cannot be removed yet, oldest xmin: xxx" 消息,则表示 vacuum 正在运行,但无法清理死元组。
要查找 vacuum 阻止程序,您可以使用 postgres_get_av_diag() 函数。有关更多信息,请参阅识别并解决 RDS for PostgreSQL 中的激进 vacuum 阻止程序。
增加 autovacuum worker 数量并移除未使用的索引
当需要执行 vacuum 操作的数据库或表超出 autovacuum_max_workers 配额时,引擎只有在 worker 可用时才会处理下一个数据库或表。在高事务率的环境中,vacuum 可能会出现事务回卷问题。
要解决此问题,请增大 autovacuum_max_workers 参数,以增加并行的 autovacuum worker。
如果您的表包含大型索引,最佳做法是移除未使用的索引。
增大 vacuum 内存
为确保引擎将调整后的内存分配应用于新的 autovacuum,请增大 autovacuum_work_mem 参数。
要为 vacuum 手动操作(例如 vacuum freeze)增加 maintenance_work_mem,请运行以下命令:
SET maintenance_work_mem TO '2GB';
**注意:**maintenance_work_mem 的默认值为 64MB。vacuum 完成后,请将该值更改回先前的值。如果不更改该值,则规格较小的实例可能会出现内存问题。
要启动 vacuum 操作,请运行以下命令:
VACUUM FREEZE VERBOSE table_name;
如果存在需要清理的大型索引,则 autovacuum 必须执行多次扫描。在 PostgreSQL 16 及更早版本中,vacuum 操作所能使用的 maintenance_work_mem 配额为 1GB。PostgreSQL 17 及更高版本使用 TidStore,它会动态分配内存,而不使用单一分配数组,从而使 vacuum 能够更高效地使用 maintenance_work_mem。
启用并行 vacuum
对于 vacuum 手动操作,您可以启用并行 vacuum,以便 PostgreSQL 为每个索引分配一个 vacuum worker 并进行并行处理。
要启用并行 vacuum,请根据数据库实例的可用 vCPU 数量和表上的索引数量调整 max_parallel_maintenance_workers 设置。
有关更多信息,请参阅 Amazon RDS for PostgreSQL 和 Amazon Aurora PostgreSQL 中的并行 vacuum。
监控数据库使用的事务数量
当 autovacuum 操作无法完成 vacuum 时,数据库可能会继续增加。您可以使用 Amazon CloudWatch 指标 MaximumUsedTransactionIDs 来获取数据库使用的最大事务数量。
此外,请为 MaximumUsedTransactionIDs 指标创建 CloudWatch 警报,以便在事务数量达到您指定的值时发送通知。有关更多信息,请参阅在 Amazon RDS for PostgreSQL 中实施事务 ID 回卷预警系统。
要在 Amazon RDS 控制台上监控 MaximumUsedTransactionIDs,请完成以下步骤:
- 打开 Amazon RDS 控制台。
- 在导航窗格中,选择 Databases(数据库),然后选择您的数据库。
- 在 Logs and Events(日志和事件)选项卡中,您可以在 Recent events(近期事件)部分中查看 MaximumUsedTransactionIDs。
如果您已达到 MaximumUsedTransactionIDs 配额,则必须对表执行 vacuum 操作。
要识别 vacuum 阻止程序,请参阅识别并解决 RDS for PostgreSQL 中的激进 vacuum 阻止程序。
