PostgreSQL 中可见性映射表(Visibility Map,简称 VM) 的使用时机和使用者。
可见性映射表(VM)是什么?
VM 是一个为每个堆表(主数据文件)分配的附属位图文件,它为数据文件中的每个页面(通常为 8KB) 跟踪两个关键的状态位:
- 全部可见位:如果某个页面的这个位被设置(=1),表示该页面中所有的元组对所有活动事务都是可见的。这意味着这个页面里没有需要被
VACUUM清理的过期元组(死元组)。 - 全部冻结位:如果某个页面的这个位被设置(=1),表示该页面中所有的元组都已经被“冻结”。冻结的元组对于任何未来的事务都被视为已提交,这主要用于防止事务 ID 回卷(wraparound)这一严重问题。
什么时候使用?
VM 主要在以下三个关键场景中被使用:
-
加速 VACUUM 操作(最重要的作用)
- 当
VACUUM(常规或 autovacuum)运行时,它会扫描 VM。 - 对于被标记为 “全部可见” 的页面,
VACUUM会直接跳过。因为它知道这些页面里没有死元组,无需进行耗时的扫描和清理。这极大地减少了VACUUM的 I/O 开销和处理时间,尤其是对于大部分数据都很静态的表。 - 对于需要防止事务 ID 回卷的
VACUUM操作,它会寻找那些尚未被标记为 “全部冻结” 的页面,并将其中的元组冻结,然后在 VM 中设置“全部冻结”位。
- 当
-
支持仅索引扫描
- 当一个查询可以完全通过索引来满足时(即仅索引扫描),优化器会考虑使用 VM。
- 如果索引指向的堆表页面在 VM 中被标记为 “全部可见”,那么数据库引擎就可以确信该索引元组对应的堆元组是有效的、可见的,而无需再访问堆表页面去检查元组的可见性(即检查 xmin/xmax)。
- 这避免了额外的堆页面访问,可以显著提升纯索引查询的速度。
-
指导防止事务 ID 回卷的清理
- PostgreSQL 使用事务 ID(XID)来实现多版本并发控制(MVCC)。XID 是一个 32 位的计数器,存在回卷风险。为了防止这个问题,必须定期将旧的元组“冻结”。
VACUUM在执行防回卷任务时,会优先处理那些离回卷点最近、且没有被 VM 标记为“全部冻结” 的页面。VM 使得VACUUM能够高效地定位需要处理的目标,而不是盲目地扫描整个表。
由谁来使用(更新和读取)?
VM 的生命周期由 PostgreSQL 的核心进程和操作管理:
-
设置者/更新者:
VACUUM:这是最主要的设置和更新者。- 当一个页面被
VACUUM扫描并确认其中没有死元组时,它会设置该页面的“全部可见”位。 - 当一个页面中的所有元组都被冻结后,它会设置该页面的“全部冻结”位。
- 当一个页面被
- 数据修改操作:当页面发生变更时,VM 位会被清除。
- 当执行
INSERT、UPDATE或DELETE操作,修改了某个堆页面时,该页面在 VM 中的 “全部可见”位会被清除(设置为 0)。因为新的操作可能在这个页面上创建了不可见的元组(例如,一个新插入的元组可能对某些事务还不可见),或者产生了死元组,破坏了其“全部可见”的状态。 - 注意:
UPDATE和DELETE操作通常不会直接设置 VM 位,它们只是通过清除位来“污染”页面状态,真正的设置工作留给后续的VACUUM来完成。
- 当执行
-
读取者/使用者:
VACUUM进程:如上所述,它读取 VM 来决定哪些页面可以跳过,哪些需要清理或冻结。- 查询执行器:在执行查询计划时,特别是当优化器选择仅索引扫描路径时,查询执行器会读取 VM 来快速判断索引元组的可见性,从而决定是否避免访问堆页面。
- SQL 命令
pg_visibility:这是一个管理工具,允许数据库管理员直接查询和分析特定表的 VM 状态,用于诊断和维护。
总结
| 方面 | 描述 |
|---|---|
| 核心作用 | 标记没有死元组和元组已冻结的页面,作为元数据缓存。 |
| 主要使用时机 | 1. VACUUM 运行时(跳过清理、指导冻结)。2. 执行仅索引扫描查询时。 3. 进行防事务ID回卷清理时。 |
| 主要使用者 | 1. 设置者:VACUUM 操作。2. 清除者: INSERT/UPDATE/DELETE 等数据修改操作。3. 读取者: VACUUM 进程、查询执行器(仅索引扫描)。 |
简而言之,VM 是 VACUUM 和查询优化器的“助手”。它通过记录页面的清洁状态,让 VACUUM 工作得更智能、更快速,同时也让某些特定类型的查询跑得更快。

961

被折叠的 条评论
为什么被折叠?



