本指南回答了一个问题: 什么是阻止清空删除死元组?
在 autovacuum 正在运行但膨胀仍持续增加时使用。 这种情况——真空已启用但未进行清理——通常只有少数几种明确的原因,本指南会逐一排查这些原因。
用一段话解释其机制
清空无法删除任何打开的快照仍可能需要的行版本。 整个服务器中最旧的此类快照是 xmin 地平线。 任何阻止该时间地平线推进的因素——无论是未结束事务、已准备事务,还是持有 WAL 的复制槽——都会使所有表的清理无法进行,不管 autovacuum 运行得多频繁,或你把它调得多激进。 死元组不断累积,膨胀加剧,事务 ID 年龄不断逼近回卷点。
这就是为什么在这里把 autovacuum 调得更激进一些也没有帮助。 问题不是吞吐量。 这是权限。
本指南介绍的症状
- autovacuum 正常运行时,膨胀却仍在增长
- 在 Monitor autovacuum 中,已死亡但未被移除的元组持续为非零
- 最旧的后端
xmin持平而非增长 - 事务 ID 年龄稳步攀升
- 出现防缠绕或故障安全真空装置
开始之前
启用会话数据和剩余事务。 请参阅 “使用故障排除指南”。
确认 未推进后,请从 xmin 进入此处。 如果 xmin正在 推进,则存在吞吐量问题,该指南是正确的。
本指南的组织方式
| 选项卡 | 它所回答的问题 |
|---|---|
| 紧急自动清理和回卷 | 这有多紧迫? |
| 自动清理阻止程序 | 具体来说,是什么在托住地平线? |
在这两个选项卡下,本指南将分析分解为命名部分:
| Section | 封面 |
|---|---|
| 最早的活动事务可见性 | 向导是否可以看到阻止 Horizon 前进的事务,以及如果看不到,需要启用什么。 |
| 主服务器长时间运行的事务 | 阻止在主服务器上运行的事务。 |
| 读取副本中的长时间运行事务 | 阻止在副本上运行的事务,这些事务在启用 hot_standby_feedback 时会阻塞主库。 |
| 孤立的已准备事务 | 两阶段提交事务处于未决状态。 |
| 复制延迟时间 | 备用服务器落后并保持地平线。 |
| 未激活的复制槽 | 槽位保留 WAL,没有使用者。 |
指南的横幅标题直接点明了责任对象。 例如,“进程 ID N 正在运行事务 ID X ,以防止 autovacuum 清理死元组”将特定的 PID 与阻塞的地平线相关联。
四个原因
每个阻碍因素都属于以下类别之一。 按照列表顺序逐项处理。 它们是按照作为答案出现的频率以及你解决它们的难易程度来排序的。
| 原因 | 典型签名 | 在重启后保留 |
|---|---|---|
| 长时间运行的事务 | 具有很旧的 xact_start 的 PID,通常 idle in transaction |
否 |
| 孤立的已准备事务 | 高于 0 的 max_prepared_transactions,pg_prepared_xacts 中的条目 |
Yes |
| 非活动复制槽 | 带有 active = false 和旧 xmin 的插槽 |
Yes |
存在 hot_standby_feedback 的复制延迟 |
副本查询会阻碍主数据库 | 否 |
孤儿预备事务和非活动槽位在重启后仍然保留。 如果重启后这个问题看似已被“修复”,但臃肿现象又立即出现,请先检查那两项。
演练:重启后幸存下来的膨胀
评估紧迫性。 紧急自动清理和回卷选项卡显示有一个数据库的事务 ID 已远超 2 亿,而且还在持续增加。 目前还不算严重,但这一趋势有一个硬性截止时间,因为一旦发生回卷,写入就会停止。
阅读横幅。 Autovacuum 阻止程序选项卡不报告长时间运行的事务,这消除了最常见的原因。 它标记
max_prepared_transactions大于零。确认。 你查询
pg_prepared_xacts,发现了一笔几天前遗留下来的预备事务,这是某个应用程序在两阶段提交进行到一半时崩溃后留下的。 由于已准备事务在重启后仍然保留,先前那次重启并没有清除它,这也就解释了为什么问题会立刻再次出现。解决。 您以其所有者身份连接到创建它的数据库,并对该
ROLLBACK PREPARED执行gid。验证。 回到 Monitor autovacuum 中,最旧的后端进程
xmin又开始推进了。 在接下来的几个小时里,autovacuum 会逐步处理积压任务,膨胀率也随之下降。
选项卡参考
紧急自动清理和包装
每个数据库距离触发紧急自动清理或回卷保护还有多近。 这是你的紧迫信号:它会告诉你,你是有几周时间来规划,还是只有几个小时来采取行动。
PostgreSQL 有大约 20 亿个可用事务 ID。 随着 age 值不断升高,系统会自行逐步升级:先启用防回绕 vacuum 机制,然后在 vacuum_failsafe_age 时触发故障保护机制(默认值为 16 亿),届时将完全取消成本节流。 如果故障保护机制已触发,请将其视为一次故障事件:下一阶段将拒绝写入请求。
为已使用事务 ID 的最大值设置指标警报,以便您通过监控发现这一问题,而不是等到发生故障时才知道。
自动清理阻止程序
列出当前阻塞项及建议的缓解措施。 如果未发现任何问题,指南会明确说明这一点,而这本身就很有用,因为它会引导你回到 Monitor autovacuum,并将其作为吞吐量问题来处理。
长时间运行的事务
本指南将这些事务分为 主服务器长时间运行的事务 和 只读副本长时间运行的事务,因为缓解措施有所不同。 主服务器上的阻止程序是本地的。 副本服务器上的阻塞会通过 hot_standby_feedback 传到主服务器,因此你必须到副本服务器上将其清除。
在任一操作之前,请检查 最早的活动事务可见性。 如果引导程序看不到阻塞 horizon 的事务,那么即使存在阻塞事务,下面的各部分看起来也会是空的。 默认情况下,超过指南阈值的事务(默认为 2 小时)被视为长时间运行的事务。 该图表将连接持续时间()与事务持续时间(collection_time - xact_startcollection_time - backend_start)分开,这两者均与采样pg_stat_activity。 阻止清理的是事务持续时间;在使用连接池时,仅连接持续时间较长是正常现象。 处于空闲状态的会话 idle in transaction 就是典型情况:什么都不做,却阻塞一切。
立即缓解。 请先取消当前正在运行的语句。 这是两个选项中干扰较小的一个,因为会话及其事务都得以保留:
SELECT pg_cancel_backend(<pid>);
如果会话处于事务空闲状态,或者取消操作也无法释放资源,请终止整个后端进程:
SELECT pg_terminate_backend(<pid>);
Caution
终止后端会回滚其未完成的事务。 在结束会话之前,请先确认该会话当前正在执行什么操作。 PID 可能属于长时间运行的迁移任务,或属于会从头开始重新启动的批处理作业。
设置防护措施,防止再次发生。 设置会话和事务可以运行多长时间的限制:
| 参数 | 它所限定的范围 | 备注 |
|---|---|---|
statement_timeout |
单个语句 | 最广泛的安全网。 如果某些作业确实会运行较长时间,应按角色或按会话进行设置,而不是在整个服务器范围内统一设置。 |
idle_in_transaction_session_timeout |
处于空闲状态且存在未提交事务的会话 | 默认值为 0 (从不)。 此设置通常可防止清理被阻止。
300000 (5 分钟)是一个常见的起点。 |
idle_session_timeout |
无事务的空闲会话 | PostgreSQL 14 及更高版本。 回收已废弃客户端占用的连接槽位。 |
transaction_timeout |
事务总持续时间 | PostgreSQL 17 及更高版本。 |
对于长期解决方案,请使用 索引调优 和 EXPLAIN (ANALYZE, BUFFERS) 来缩短那些确实需要长时间运行的事务,并将大型批处理事务拆分成较小的事务。
注释
在只读副本上长时间运行且带有hot_standby_feedback = on的事务会阻碍主库上的xmin horizon。 如果主节点显示处于阻塞状态,且找不到本地原因,请检查 读取副本上的长时间运行事务 部分。 要填充它,请在副本上启用 metrics.collector_database_activity,并将其 Sessions 数据流式传输到 Log Analytics。
孤儿预备事务
预处理事务属于两阶段提交的一部分。 准备好的事务会保留其 XID 和锁,直到显式提交或回滚该事务,并在 服务器重启后继续运行。 如果客户端在 PREPARE TRANSACTION 到其完成处理之间发生崩溃或断开连接,该事务就会变成孤儿事务,并会无限期地阻塞冻结和清理。
本指南检查是否 max_prepared_transactions 大于 0。 若要确认,请在“灵活服务器>设置”下检查顾问建议,或直接查询:
SELECT gid, prepared, owner, database
FROM pg_prepared_xacts
WHERE prepared <= NOW() - INTERVAL '1 hour';
通过以其所有者或超级用户身份连接到创建该事务的数据库来解决此问题:
ROLLBACK PREPARED 'gid'; -- or, if the transaction should complete
COMMIT PREPARED 'gid';
Caution
提交孤立的准备事务将应用几天或几周前暂存的更改。 除非你确定它本应完成,否则就回滚。
如果不使用两阶段提交,请将 max_prepared_transactions 设置为 0,以防止此问题再次发生。
未激活的复制槽
复制槽会一直保留 WAL,直到其订阅者将其消费完。 当订阅者停止消费时,这种保证就会变成一种负担:该槽会保留 xmin 对应的界限,并无限期保留 WAL,从而占用存储并阻碍清理。
这两种类型失败的方式不同:
- 逻辑槽 会膨胀 目录 表并保留 WAL。
- 物理槽会导致用户表膨胀、保留 WAL,并降低复制性能。
列出各插槽及其状态:
SELECT slot_name, slot_type, database, active, age(xmin) AS xmin_age
FROM pg_replication_slots
ORDER BY age(xmin) DESC NULLS LAST;
根据你发现的内容进行操作:
真正未使用的非活动逻辑槽 可以安全地删除:
SELECT pg_drop_replication_slot('slot_name');未激活的物理槽。请勿手动删除这些槽。 请提交支持请求,以便进行调查和清理;如果不再需要,也可以删除只读副本实例。
Warning
删除仍在使用的复制槽会导致其订阅者的复制中断,随后可能需要进行一次完全重新同步。 在移除槽位之前,请确认其确已废弃。
应定期监控槽位,而不是仅在发生故障时才监控。 被弃用的槽位通常会一直悄无声息,直到存储占用或数据膨胀问题引起关注。
复制延迟
当备用服务器在写入、刷新和应用更改方面落后时,就会发生复制滞后。 通过使用 hot_standby_feedback = on,副本会将其最早的未完成事务报告给主库,这样主库就不会清理掉副本仍然需要的行。 此设置按设计方式工作,因为它会阻止副本上的查询取消,但它会将副本的事务规则传输到主副本的清理中。
查找阻碍因素:
-- On the primary: oldest transaction needed per slot.
SELECT slot_name, slot_type, database, xmin, active
FROM pg_replication_slots
ORDER BY age(xmin) DESC;
-- On the replica: sessions holding an old snapshot.
SELECT pid, datname, usename, state, backend_xmin
FROM pg_stat_activity
WHERE backend_xmin IS NOT NULL
ORDER BY age(backend_xmin) DESC;
使用 pg_cancel_backend() 或 pg_terminate_backend() 取消或终止阻塞会话。
持续减少逻辑复制延迟:
- 最大程度地减少长时间运行的事务。 设置
statement_timeout和拆分大型事务。 - 使用
EXPLAIN (ANALYZE, BUFFERS)索引和分区优化查询和表设计。 - 保持主副本和副本的计算资源对等。 规模过小的副本本来就跟不上。
- 调整复制参数,先在暂存环境中测试:
max_worker_processes、max_replication_slots、logical_decoding_work_mem、max_sync_workers_per_subscription和max_parallel_apply_workers_per_subscription(PostgreSQL 16 及更高版本)。 - 避免
REPLICA IDENTITY FULL。 优先使用带有REPLICA IDENTITY USING INDEX的主键或唯一索引。FULL将每一行的每一列写入 WAL。 - 将工作负载分配到多个槽位中。
- 监视
pg_replication_slots、pg_stat_replication_slots和pg_stat_replication,并查看日志中是否有复制错误。 - 在非高峰期计划批量操作。
- 通过定期进行
VACUUM和ANALYZE,使统计数据保持最新。
优秀的标准是什么样的
| 信号 | 正常 | 调查 |
|---|---|---|
最旧的后端 xmin |
以吞吐量推动发展 | 平坦 |
| 持续时间最长的未完成事务 | 会议记录 | 小时 |
会话 idle in transaction |
无持续现象 | 任何、持续 |
pg_prepared_xacts |
为空,或很快完成解析 | 超过一小时的条目 |
| 复制插槽 | 全部 active = true |
任何带有旧 xmin 的未激活项 |
| 已使用的最大事务 ID | 低于 2 亿 | 超过 2 亿 |
本指南无法告诉你的内容
- 终止会话是否安全。 本指南会指出阻碍项;终止它会带来什么影响,需要由你自行评估。
- 副本正在做什么。 副本会话需要在副本上启用
metrics.collector_database_activity,并流式传输其自身的 Sessions 数据。 - 恢复需要多长时间。 清除阻止程序后,在严重膨胀的服务器上处理累积的死元组可能需要几个小时。
- 膨胀是否会自行逆转。 清空回收空间以供重复使用,但不会将其返回到文件系统。 严重膨胀的表可能需要
VACUUM FULL或pg_repack。 - 子样本阻隔剂。 会话数据是经过采样的;在两次采样之间出现并消失的阻碍因素可能无法被捕获。