High CPU and Slow Queries with `ERROR: must be a superuser to terminate superuser process`
当你遇到高 CPU 使用率、查询性能缓慢以及关于自动清理的 ERROR: must be a superuser to terminate superuser process 消息时,这表明在你的 Postgres 数据库上有一个关键且无法终止的自动清理操作正在运行。本指南将解释为什么会发生这种情况,以及你可以采取哪些步骤。
🌐 When facing high CPU utilization, slow query performance, and an ERROR: must be a superuser to terminate superuser process message regarding an autovacuum, it indicates that a critical, non-terminable autovacuum operation is running on your Postgres database. This guide explains why this happens and what steps you can take.
Postgres 核心概念 #
🌐 Core Postgres concepts
要理解这个问题,弄清几个核心的 Postgres 概念是必须的:
🌐 To understand this issue, it's essential to grasp a few core Postgres concepts:
什么是 MVCC(多版本并发控制)? Postgres 使用 MVCC,这让多个事务可以同时访问同一条数据,而不会互相锁住。Postgres 不会直接修改原来的行,而是在每次数据被修改或删除时创建该行的新版本。旧版本会保留着,供在更改之前启动的其他事务访问。
什么是死元组? 旧版本的行,不再对任何活跃事务可见,就称为“死元组”(或死行)。这些死元组会占用磁盘空间,如果不清理,可能会影响性能。
什么是自动清理(Autovacuum)? 自动清理是 Postgres 中的一系列后台进程,旨在自动回收死元组占用的存储空间,并更新查询规划器的统计信息。当表中的死元组数量超过某个可配置的阈值时(例如表总行数的一定百分比,在一些默认配置中可能是 20%),它会自动运行。
什么是事务ID(XID)回绕? 在 Postgres 中,每个事务都会被分配一个唯一的事务ID(XID)。这些 XID 是 32 位整数,也就是说数量是有限的(大约 40 亿个)。如果数据库不断创建新的事务,而旧的事务又没有被“冻结”(标记为永久可见),XID 最终可能会“回绕”——也就是说,新事务会被分配到数值比非常老、仍在使用的事务还要小的 XID。这会导致数据库无法判断哪些行是可见的,哪些不可见,从而可能损坏数据,使数据库无法使用。
为了防止这个严重问题,Postgres 会启动一个特殊的自动清理操作:“环绕防护清理”。
🌐 To prevent this critical issue, Postgres initiates a special autovacuum operation: the "wraparound prevention vacuum."
理解问题:一个关键的自动清理 #
🌐 Understanding the problem: A critical autovacuum
当你遇到与自动清理(autovacuum)相关的 ERROR: must be a superuser to terminate superuser process,并标记为“防止回绕”时,这表示这个必需的、系统关键的操作正在进行中。
🌐 When you encounter the ERROR: must be a superuser to terminate superuser process associated with an autovacuum marked "to prevent wraparound," it signifies that this mandatory, system-critical operation is underway.
症状和原因:
- 高资源使用率: 你通常会注意到 CPU 使用率很高(例如,接近 100%)以及磁盘 I/O 增加,因为自动清理进程会扫描并清理数百万行数据。内存通常保持稳定。
- 慢查询: 当自动清理占用大量资源时,其他数据库查询会变得明显更慢,可能会导致应用停机。
- 不可终止的进程: 由于其在防止数据损坏方面的关键作用,环绕防护自动真空进程不能被终止,即使是超级用户也不行。尝试停止它会导致出现
ERROR: must be a superuser to terminate superuser process消息(或者数据库会立即重新启动它)。这个进程 必须 让它完成。
为什么它会运行?
这种情况通常发生在大表或者高写入量的表中(例如 your_table,可能有数百 GB 大小,包含数亿行数据),这些表会快速积累死元组。当表的事务 ID 年龄接近关键阈值时,Postgres 会自动触发这个紧急自动清理。例如,如果一个表有数百万行数据,并且死行超过一百万,超过其配置的 autovacuum_vacuum_scale_factor(比如 0.2),可能会触发常规自动清理。然而,如果 XID 年龄继续增加,系统会优先进行防止回绕的清理,以保障数据完整性。
在关键自动清理期间减轻性能影响 #
🌐 Mitigating performance impact during a critical autovacuum
由于无法停止环绕防护自动清理,最好的方法是给数据库提供足够的资源,以尽可能高效地完成操作。
🌐 Since the wraparound prevention autovacuum cannot be stopped, the best approach is to provide the database with sufficient resources to complete the operation as efficiently as possible.
- 升级你的数据库计算实例:
- 操作: 暂时提升你的实例 CPU(例如,从
m6g.4xlarge升到m6g.8xlarge或更高)。 - 为什么有帮助: 更多的 CPU 核心和处理能力可以让自动清理操作运行得更快,从而减少它对数据库的整体影响时间。
- 注意事项: 这通常会导致短暂的停机(通常为1-2分钟),因为实例需要重启。不过,autovacuum 进程设计上会自动暂停并恢复。
- 操作: 暂时提升你的实例 CPU(例如,从
- 提高磁盘吞吐量/IOPS:
- 操作: 如果磁盘 I/O 利用率也一直很高(例如接近 100%),可以考虑暂时增加磁盘的预配置 IOPS 和吞吐量。
- 为什么有帮助: 自动清理是一个I/O密集型操作,涉及大量的读写。更高的磁盘性能可以显著加快这个过程。
- 注意事项: 云服务提供商可能会限制磁盘修改的次数(例如,在滚动的24小时内最多可修改四次)。只要你没有超过这个滚动24小时的配额,上一次修改完成后就可以立即开始新的修改。
监控进展和未来预防 #
🌐 Monitoring progress and future prevention
监控当前自动清理进程:
你可以使用 pg_stat_progress_vacuum 视图来监控正在运行的自动清理进程的进度:
1SELECT relid::regclass AS table, round(100.0 * heap_blks_scanned / heap_blks_total, 2) AS pct_scanned FROM pg_stat_progress_vacuum;这个查询会显示表格被清理过程扫描的百分比。一旦关键表的 pct_scanned 达到100%,操作基本完成,资源使用应该会恢复正常。完成后,你可以考虑把实例和磁盘资源降回原来的配置。
🌐 This query will show the percentage of the table that has been scanned by the vacuum process. Once the pct_scanned reaches 100% for the critical table, the operation is largely complete, and resource usage should normalize. After it finishes, you can consider downgrading your instance and disk resources back to their original configuration.
未来的预防措施: 为了避免将来出现紧急环绕真空,特别是在高读写表上:
-
监控死行: 定期检查表中的死行数量。你可以使用以下 SQL 查询:
1select relname, n_live_tup, n_dead_tup, last_autovacuum2from pg_stat_all_tables3where schemaname = 'public'4order by n_dead_tup desc5limit 10;或者,如果使用 Supabase CLI,你可以运行:
supabase db inspect bloat如果一张表经常显示大量的死元组(例如,即使在做了清理之后,数亿条活行仍然存在大量死元组),这通常意味着需要主动进行维护。
-
安排手动清理(Vacuum): 对于写入活动频繁的表,可以考虑在非高峰时间安排手动
VACUUM或VACUUM FULL操作,以回收空间并防止 XID 年龄变得过高。VACUUM FULL更为激进,但会锁表并重写整个表,因此应谨慎使用并计划停机时间。 -
调整自动清理设置: 对于一直有问题的表,你们的数据库团队可能需要调整自动清理参数(比如
autovacuum_vacuum_scale_factor、autovacuum_vacuum_threshold或autovacuum_freeze_max_age),让自动清理更积极或者更早触发,防止 XID 年龄达到危险水平。