Postgres VACUUM 和 Xmin Horizon

在工作中需要了解 Postgres 中称为"xmin Horizo​​n"的概念,所以留下了一份学习记录。

xmin 地平线 也称为 最旧的 xmin,它告诉我们 "vacuum 进程可以清理死元组的 时间点(就事务而言)"。换句话说,VACUUM 无法清除此 xmin 范围之后( 即大于xmin)的死元组。当您看到类似"1165 are dead but not still moving"的日志时,这意味着有 1165 个死元组,但 VACUUM 无法清理它们,因为它们位于 xmin 范围之后。

重要的是要确保这个 xmin 范围不被阻碍,这样 VACUUM 才能完成它的工作!

死元组

在讨论 xmin 地平线 之前,让我们快速回顾一下 Postgres 中的 VACUUM 是什么。

当您删除一行时,Postgres 不会立即删除该行 ,而是会将此类行标记为已删除。一旦它被标记为已删除,它就会成为死元组,并 将被 VACUUM 删除。当你更新一行时也会发生同样的事情,因为它相当于 Postgres 中的"删除和插入"。删除的行(旧值行)将成为死元组,并将被 VACUUM 删除。

如果发生更多的删除/更新,则会积累更多的死元组。 VACUUM 是 Postgres 的一个重要进程,用于清理此类 数据行 并避免膨胀。

让我们考虑一个简单的例子。有一个名为colors表,有两列: idcolor

  1. Connection A 将两行(蓝色、红色)插入颜色表中
  2. Connection B 将红色行更新为绿色,这相当于以下步骤 2.1. Connection B 标记红色行已删除 = 死元组 2.2. Connection B 插入一行,颜色为绿色

这样,红色行现在是死元组,需要被vacuum,一旦没有人看它就会被vacuum。

这个一旦没有人在看它 正是 xmin 地平线 告诉你的。仅仅因为行被标记为已删除并不意味着 Postgres 可以清理它们,因为它们可能仍然对某人可见

xmin、xmax 和可见性

我想澄清一些术语,因为它们确实让我难以理解这里的事情。 xminxmax对我来说是最令人困惑的,并且到处都在使用,包括"xmin 地平线"和"最旧的 xmin"。

系统列中的xmin

每个表都有几个由系统隐式定义的内部列。在表的每一行中,都有名为xminxmax的列:

  • xmin :该行版本的插入事务的标识(事务 ID)。
  • xmax :删除事务的标识(事务 ID),或者对于未删除的行版本为零。

这些值与每一行相关联,并且与 xmin 地平线 没有直接关系 ,因此当我们谈论 xmin Horizo​​n 或最旧的 xmin 时,让我们忘记这些值(我几乎想将它们称为xinsertingxdeleting )。

快照的xmin

快照是数据库的特定时间/版本。将为每个查询生成一个快照(使用默认的 READ COMMITTED 事务隔离 ),并且查询将查看此快照以返回结果。这允许每个查询都具有一致的数据库视图,并且在查询执行期间永远不会看到任何未提交的数据。

在下图中,当 Q2 运行时,即使 Q3 更新了 table1 的值,Q2 也不会看到该值,因为它不在 Q2 的快照中。

快照 也有 自己的 xminxmax这个和xmin地平线有关

  • xmin :仍处于活动状态的最小事务 ID。所有小于 xmin 的事务 ID 要么已提交且可见,要么已回滚并终止。
  • xmax :超过已完成的最大事务 ID。截至快照时,所有大于或等于 xmax 的事务 ID 尚未完成,因此不可见。

将其应用到上图:

  • t1
    • XID 49 的 Q1 已提交,xmin 变为 50
    • Q2 从 xmin 50 的快照开始(值为 1)
  • t2
    • XID 50 在 Q3使 已提交,xmin 变为 51
    • Q4 从 xmin 51 的快照开始(带有 value2)

这样,我们就可以知道谁在看什么。在 t2 点,value1 行版本被"删除",但 Q2 仍在查看 xmin 50 的 value1 行版本,因此 t2 的 xmin 范围(或最旧的 xmin)将为 50。 在 t3 点,由于 Q2 已经完成并且没有人在看 xmin 50 行版本,因此 xmin Horizo​​n 变为 51。

阻挡 xmin 地平线

我们讨论了 Postgres 如何创建死元组。我们还讨论了 xmin 地平线是什么。现在让我们讨论一下什么会阻碍 xmin 地平线 并可能阻止 VACUUM 有效运行。

有两个方面定义 xmin 地平线:

  • Xmin地平线 源自我自己(主服务器)
  • Xmin Horizon 源自副本

对于源自我自己(主服务器)的 xmin 地平线​​ 来说,有两件事会阻碍 xmin 地平线:

  • 长时间运行的事务
    • pg_stat_activity.backend_xid :后端的事务ID。当该事务仍在运行时,您当然无法清理与该事务相关的行。
    • pg_stat_activity.backend_xmin :当前运行的查询或事务的快照 xmin。这已进行了解释。
  • 未完成的准备事务
  • pg_prepared_xacts.transaction :为两阶段提交准备的事务。如果它未完成并保留下来,它可能会成为最旧的 xmin。由于准备好的事务相当罕见,因此您在大多数情况下无需担心这种情况。

对于源自副本的 xmin 地平线 来说,还有两件事可以阻碍 xmin 地平线 的向前推进。

  • 延迟/错误的复制槽
    • pg_replication_slots.xmin :该槽需要数据库保留的最旧的事务。当复制延迟或备用服务器关闭时,该值可能会"卡住",因此可能会发生阻止。该值仅与hot_standby_feedback = on物理(流式)复制相关。 -pg_replication_slots.catalog_xmin :影响该槽需要数据库保留的系统目录的最旧事务。与上面的xmin不同,该值对于逻辑复制也很重要。
  • 长时间运行的事务( hot_standby_feedback = on的备用)
    • pg_stat_replication.backend_xmin :备用服务器的 xmin 范围。当hot_standby_feedback`打开时,备用数据库上的查询在 xmin 范围内的行为将与主数据库相同。如果备用数据库上的查询正在查看特定行版本,则无法通过 VACUUM 删除它们。

您可以查看Laurenz Albe 的博客文章,了解运行以检查这些值的示例查询,以及解决问题所需采取的操作。

autovacuum运行过于频繁?

实际上,我们最近在工作中遇到了一个问题,即临时数据库运行 autovacuum 的频率太高。我检查了 autovacuum 日志,并注意到以下日志行:

yaml 复制代码
automatic aggressive vacuum of table "template0.pg_catalog.pg_shdepend": index scans: 0
pages: 0 removed, 29 remain, 0 skipped due to pins, 0 skipped frozen
tuples: 0 removed, 3892 remain, 1165 are dead but not yet removable, oldest xmin: 108204095
index scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removed
I/O timings: read: 0.000 ms, write: 0.000 ms avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s
buffer usage: 124 hits, 0 misses, 0 dirtied
WAL usage: 0 records, 0 full page images, 0 bytes
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s

该日志行的重要部分是 1165 are dead but not yet removable, oldest xmin: 108204095

有趣的是, pg_catalog相关表经常发生自动清理。我们检查了上面提到的地方,发现 pg_replication_slots.catalog_xmin 正是108204095 。发生这种情况是因为逻辑复制到的副本库 少一列(该列被添加到主库,但在备库没有添加),并且复制不久前已停止("错误复制槽"情况)。

一旦我们手动将列添加到副本,复制就会恢复并且catalog_xmin开始前进。主数据库最终能够使用pg_catalog表运行 autovacuum,并且 autovacuum 频率恢复正常。日志行也变成了"0 已死亡但尚未可移除",这意味着 VACUUM 能够清理它想要清理的任何内容。

Postscript/References 后记/参考文献

关于 xmin Horizo​​n 有几篇很棒的博客文章,您可以查看它们以了解更多信息。


原文:Postgres VACUUM and Xmin Horizon · Keiko in Somewhere

相关推荐
zxrhhm1 小时前
PostgreSQL 中约束Constraints
数据库·postgresql
PGCCC12 小时前
【PGCCC】Postgresql BRIN 索引原理
数据库·postgresql
晚风_END1 天前
postgresql|数据库开发|python的psycopg2库按指定顺序批量执行SQL文件(可离线化部署)
服务器·开发语言·数据库·python·sql·postgresql·数据库开发
张彦峰ZYF2 天前
解读InnoDB数据库索引页与数据行的紧密关联
数据库·sql·mysql·postgresql·oracle
程序员学习随笔3 天前
PostgreSQL技术内幕19:逻辑备份工具pg_dump、pg_dumpall
数据库·postgresql
小怪兽ysl4 天前
【PostgreSQL使用pg_filedump工具解析数据文件以恢复数据】
数据库·postgresql
福如意如我心意4 天前
PostGres命令【常用维护,增删改查】
数据库·postgresql·psql
晴天飛 雪4 天前
Grafana监控PostgreSQL
数据库·postgresql·grafana
黎明晓月4 天前
PostgreSQL提取JSON格式的数据(包含提取list指定索引数据)
postgresql·json·list