news 2026/7/3 17:48:45

PostgreSQL 数据库 CPU 异常升高问题分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PostgreSQL 数据库 CPU 异常升高问题分析

在生产环境中数据库 CPU 利用率在某个时间点突然异常增高,几分钟后恢复正常。这种情况导致在这几分钟内,服务可用性下降。由于数据库使用的是 AWS RDS(postgresql),通过 Database Insights 查看异常时间点的 Top SQL,发现存在 autovacuum 事件。最终分析到原因是:在高事务推进速度下,常规 vacuum 无法把业务表的冻结年龄持续压低,最终周期性逼近防回卷区间,触发anti-wraparound autovacuum,从而引起短时 CPU/WAL/IO 峰值

PostgreSQL 面临“事务ID回卷”的风险,需要主动进行“冻结”来防范;而 MySQL (InnoDB) 则没有这个问题。这种差异源于两者在实现 MVCC 时不同的核心架构选择。

下面是具体的分析过程。

XID 回卷

PostgreSQL 的 MVCC(Multi-Version Concurrency Control) 依赖 XID,即事务 ID。

举例说明:

假设 user 表中有一行数据,其由第 50 个事务创建。

id name 1 Tom

现在第 100 个事务修改了这条记录,将 name 改成了 Jerry。在 PostgreSQL 中并不会直接覆盖原纪录,而是会有两条记录同时存在:

id name xmin xmax 1 Tom 50 100 1 Jerry 100 NULL

其中,xmin表示创建该行数据的事务 ID,xmax为删除(失效)该行数据的事务 ID。换句话说,第一行数据对事务 id 在 [50,100)区间的事务可见,第二行数据对事务 id 在 [100, NULL) 区间的事务可见。

事务 ID 回卷

在 PostgreSQL 中,事务 ID 用 32 位整数表示(约 42 亿)。当达到上限后,事务 ID 会回卷至 0。一旦回卷,新事务的 ID 就可能比旧事务的 ID 更小,从而导致在数据可见性方面产生问题。

为了解决这一问题,PostgreSQL 通过VACUUM机制,将那些足够老的事务 ID 标记为一个特殊的、对所有事务都可见的 “冻结 ID”(relfrozenxid)。PostgreSQL 为每个表都维护了一个relfrozenxid,用来表示这个表中所有事务 ID 小于relfrozenxid的记录都已经被冻结。那如何断定什么是足够老的事务 ID 呢?

Vacuum 分为两种,一种是常规的,一种是强制的(anti-wraparound VACUUM)。

常规 vaccum 是动态的,其选择冻结的事务 id 会结合不同的参数和当前系统状态算出来。常规 vacuum 消耗的资源少一些,对数据库影响相对较小。

而强制 vacuum 则是有固定规则的:表中当前活跃的最老的事务 id 的年龄接近autovacuum_freeze_max_age时,会触发强制 vacuum,以保证事务安全。强制 vacuum 会占用大量资源,导致 CPU、网络、读写延迟都会增加。

autovacuum_freeze_max_age理论上可以是最大事务 id 的一半,即 21 亿。默认情况下,该值为 2 亿,通过 sql 语句可以查询:

SHOWautovacuum_freeze_max_age;

这个值表示,当前数据库中活跃的事务 id 的范围不能超过 2 亿。假设某个表中最老的活跃事务 id 是 1 kw,当前数据库中最大事务 id 是 2.1 亿,则这个表中这条最老的事务年龄就达到了autovacuum_freeze_max_age阈值,会触发anti-wraparound VACUUM

autovacuum_freeze_max_age 最大值

理论上来说,只要允许活跃的事务 id 范围不超过最大值的一半,就可以达到循环利用事务 id 的效果。

举个例子说明,假设数据库事务 id 最大值为 12,允许活跃的事务为 6 个。如果当前活跃的事务 id 最大值为 9,最小值为 2,则 xid = 2 的活跃事务必然是比 9 更新的事务。因为只有按照 9 -> 10 -> 11 -> 12 -> 1 -> 2 的方向演进,活跃的事务范围才不超过 6。反过来如果是按照 2 到 9 的顺序演进,事务范围就大于 6 了,不满足条件。

如果允许活跃的事务 > 6 呢?假设为 7。当前活跃的事务 id 最大为 9,最小为 3。如果按照 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 的顺序演进,满足条件,因此认为 xid = 9 的事务是新的事务。反过来按照 9 -> 10 -> 11 -> 12 -> 1 -> 2 -> 3 的顺序演进,同样满足条件,因此认为 xid = 3 的事务是新的事务。这就产生矛盾了。

如果允许活跃的事务数超过事务 id 最大值的一半,就无法判断两个事务的新旧,所以autovacuum_freeze_max_age的值最大可以为 2^31。

Trouble-Shooting

想要判断是否是anti-wraparound VACUUM导致数据库 CPU 飙升,可以通过一些直观的手段来查看是否有 Vacuum 事件,比如 AWS Database Insights 监控。如果不能直观的看出,也可以通过 sql 语句来判断。

查询活跃事务 id 最老的表:

SELECT c.oid::regclass AS table_name, greatest(age(c.relfrozenxid), age(t.relfrozenxid)) AS xid_age, pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size FROM pg_class c LEFT JOIN pg_class t ON c.reltoastrelid = t.oid WHERE c.relkind IN ('r','m') ORDER BY xid_age DESC LIMIT 10;

如果某个表中xid_age接近 2 亿,说明即将或者已经触发强制 vacuum。如果还差很多,但是 xid_age 增长速度很快(间隔几分钟后再次查看),那也有可能在不久的将来会触发强制 vacuum。当然也可能触发普通 vacuum 将老龄的事务 id 给冻结了。通过以下语句可以查看该表上次普通 vacuum 的时间:

SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_dead_tup, last_autovacuum, autovacuum_count FROM pg_stat_user_tables WHERE relname = 'xxx';

结果示例如下,说明普通 vaccum 确实有触发:

relname n_tup_ins n_tup_upd n_tup_del n_dead_tup last_autovacuum autovacuum_count xxx 1675710 11324652 30733 9366 2026-06-25 10:37:58.7963+00 52

如果写少( upd/del 低)同时autovacuum_count低,说明这张表不容易被普通 vacuum 清理。与此同时如果全局 xid 推进速度快,那这张表就很容易被动变老。

再看看全库 xid 推进速度:

SELECT datname, xact_commit, xact_rollback, blks_read, blks_hit, tup_inserted, tup_updated, tup_deleted FROM pg_stat_database ORDER BY (xact_commit + xact_rollback) DESC;

结果如下:

datname xact_commit xact_rollback blks_read blks_hit tup_inserted tup_updated tup_deleted "rds" 2646817444 1329881 12193124 37985177484 121269311 15274081 1423400

xact_commitxact_rollback高说明全库事务推进快。tup_insertedtup_updatedtup_deleted高说明全局写活动频繁。

结果分析

根据以上结果可得结论如下:

业务表xid_age高且增长速度快说明该表确实是高龄表,已经进入中高风险区间,具备触发防回卷清理的前提。

业务表写多(相对其他表来说),autovacuum_count多,last_autovacuum时间近,说明不是没有触发 autovacuum,而是触发了很多次但仍然老化偏高。

原因是全库事务推进速度太快,即使业务表在持续 vacuum,也可能很快再次变老。随着 age(relfrozenxid) 接近autovacuum_freeze_max_age阈值,系统触发 anti-wraparound autovacuum。

要验证也很容易,只需要查看数据库 events 或者 logs,比如 AWS Log Insights 可以搜索:

fields @timestamp, @message | filter @message like /wraparound/ # | filter @message like /autovacuum/ | sort @timestamp desc | limit 100

看看是否有防回卷清理或普通清理。

优化措施

事实上不管是普通 vacuum,还是强制 vacuum,都会导致 CPU 升高,只不过强制 vacuum 更严重一些。

可以通过调整 vacuum 相关的 RDS 参数来优化资源占用:

参数含义默认值调整效果
autovacuum_vacuum_scale_factor触发 vacuum 阈值的比例项0.1vacuum 阈值 =autovacuum_vacuum_threshold+scale_factor× 表行数,scale_factor越小越早触发 vacuum,每次 vacuum 就更平滑
autovacuum_vacuum_threshold触发 vacuum 阈值基础项PostgreSQL 默认 50正常无需调
autovacuum_vacuum_cost_limitvacuum 的成本值GREATEST({log(DBInstanceClassMemory/21474836480)*600},200)这个值本身是根据实例内存来计算的,内存越大,允许 vacuum 占用资源就越多,这是一个经验值,不建议调整
autovacuum_vacuum_cost_delayvacuum 的成本值达到后暂停时间继承 PostgreSQL 的 2msautovacuum_vacuum_cost_limit配合作用,本次 vacuum 消耗达到cost_limit后暂停cost_delay时间后再继续,越大越平滑,但清理更慢
autovacuum_work_mem每个 autovacuum worker 的工作内存默认与maintenance_work_mem一样如果太小,autovacuum 可能需要多次扫描索引,导致执行时间变长和 I/O 增加

优化思路是提高 vacuum 频次,降低单次 vacuum 体量。因此考虑如下调整:

  • 对热点大表降低autovacuum_vacuum_scale_factor,增加 vacuum 频率以减少单次 vacuum 体量。
  • 适当降低autovacuum_vacuum_cost_limit并提高autovacuum_vacuum_cost_delay,降低瞬时 CPU/IO 冲击。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/29 0:27:42

合约跟单交易所系统:用户最关心的30个问题全解答

优链科技:合约跟单交易所系统:用户最关心的30个问题全解答 无论是新手跟单者还是资深交易员,面对合约跟单系统时总有一连串疑问。本文以问答形式,系统梳理跟单交易中最核心的30个问题,涵盖入门、操作、风控、盈亏差异、…

作者头像 李华
网站建设 2026/6/29 0:59:47

Google开源2个新项目,AI Agent硬件效率猛提97%

Google在2025年5月下旬悄悄放出了两个开源项目:Agent Substrate和AX。前者版本号v0.0.0,后者v0.1.0,全都挂在Apache-2.0协议下。这两个项目要干的事很具体——让成百上千个AI Agent共享同一份物理算力,而不是一个Agent占着一台机器…

作者头像 李华
网站建设 2026/6/29 0:54:56

图解 Transformer-中文讲解版

目录 1、从高处俯瞰整个模型 2、把张量画进流程图 3、编码器里的数据流 4、自注意力(高层直觉) 5、自注意力(向量级细节) 6、自注意力的矩阵实现 7、多头注意力 8、位置编码:让模型感知顺序 9、残差连接与层归一化 10、…

作者头像 李华
网站建设 2026/6/29 0:27:49

Metso D201136L 模块常见故障排查与运维实战

在工业现场,最让人头疼的往往不是设备彻底坏掉,而是那些时好时坏、难以复现的“软故障”。很多时候,产线突然停摆,报警灯狂闪,但当你拿着万用表赶过去时,一切又恢复正常了。这种“幽灵故障”不仅打乱生产节…

作者头像 李华
网站建设 2026/6/29 0:27:48

旋进漩涡流量计如何选型?

旋进漩涡流量计属于速度式气体专用计量仪表,自带一体化温压补偿,可自动将工况气体流量换算标况流量,想要选型精准、不出现计量偏差、设备过早损坏,核心抓介质、口径量程、温压工况、现场安装环境、信号通讯、厂家实力六大维度&…

作者头像 李华