PostgreSQL 页剪枝(Page Pruning)与 HOT 更新

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

PostgreSQL 页剪枝(Page Pruning)与 HOT 更新

背景:PostgreSQL 的 MVCC 机制

PostgreSQL 使用 MVCC(多版本并发控制)实现事务隔离。每次 UPDATE 不是原地修改,而是:

  1. 将旧行标记为"已删除"(设置 xmax)
  2. 插入一条新行(新的物理 tuple)

这意味着频繁更新会产生大量"死元组"(dead tuples),需要 VACUUM 清理。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

HOT 更新(Heap Only Tuple Update)

是什么

HOT 是一种优化机制,允许在同一数据页内 完成更新,且不需要更新索引。

触发条件(必须同时满足)

  1. 新旧版本在同一个堆页(same heap page)
  2. 被更新的列不属于任何索引的键列

实现原理

页内布局(更新前):

Index Entry → ItemId1 → Tuple_v1 (xmax=0)

更新后(HOT):

Index Entry → ItemId1 → Tuple_v1 (xmax=txid, HOT标志)

↓ (t_ctid 指向同页新版本)

ItemId2 → Tuple_v2 (xmin=txid, xmax=0)

  • 旧 tuple 的 t_ctid 不再指向自身,而是指向同页的新 tuple
  • 新 tuple 头部设置 HEAP_ONLY_TUPLE 标志
  • 旧 tuple 设置 HEAP_HOT_UPDATED 标志
  • 索引不变,仍指向 ItemId1

读取时的链式追踪

当通过索引访问时,PostgreSQL 发现 ItemId1 指向的 tuple 有 HOT 链,会沿着 t_ctid 链向前追踪,找到对当前事务可见的最新版本。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

页剪枝(Page Pruning)

是什么

页剪枝是一种轻量级的页内清理,在访问某个页时顺带执行,不需要等待 VACUUM。

触发时机

  • 当页面空间不足以插入新 tuple 时
  • 由 heap_page_prune_opt() 触发(在 heapam.c 中)

做了什么

  1. 清理 HOT 链中的死元组:如果 HOT 链头部的旧版本对所有活跃事务都不可见,则可以回收
  2. 重定向 ItemId:将死掉的 ItemId 标记为 LP_REDIRECT,直接指向链中仍存活的版本,缩短追踪路径
  3. 释放 ItemId 槽位:完全死掉的 tuple 的 ItemId 标记为 LP_DEAD,空间可被复用
  4. 整理页内碎片(可选,通过 heap_page_prune + PageRepairFragmentation)

与 VACUUM 的区别

页剪枝 VACUUM
触发方式 访问页时自动触发 后台进程或手动
范围 单个页 整张表
索引清理 不处理 处理
事务 ID 回绕保护 不处理 处理
开销 极低 较高

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

两者的关系

UPDATE 操作

│

├─ 满足 HOT 条件?

│ │

│ YES → HOT 更新(同页,不更新索引)

│ │ │

│ │ └─ 产生页内 HOT 链

│ │ │

│ │ └─ 页剪枝 → 清理链中死元组,回收空间

│ │

│ NO → 普通更新(可能跨页,更新索引)

│ │

│ └─ 需要 VACUUM 清理

HOT 更新减少了索引膨胀,页剪枝则让 HOT 链产生的死元组能被快速回收,两者配合使得高频更新场景下性能大幅提升,减少对 VACUUM 的依赖。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

关键源码位置(PostgreSQL src)

  • src/backend/access/heap/heapam.c --- HOT 更新逻辑(heap_update)
  • src/backend/access/heap/pruneheap.c --- 页剪枝核心逻辑
  • src/include/storage/bufpage.h --- ItemId 标志定义
  • src/include/access/htup_details.h --- HEAP_HOT_UPDATED、HEAP_ONLY_TUPLE 标志
相关推荐
OceanRoy35 分钟前
数据库表的设计
数据库·内连接·外连接·er图·表的约束·三大范式·多表连接
Pniubi1 小时前
怎么排查慢SQL,怎么解决?
java·数据库·sql
Decay__5 小时前
VMware 安装 SQL Server 双节点 AlwaysOn 可用性组教程 —— WSFC、AG 与侦听器完整部署(系列核心篇)
运维·数据库·sql·mysql·架构
茶栀(*´I`*)6 小时前
SQL 性能分析:从慢查询日志到执行计划的完整定位方法
数据库·sql
艺杯羹7 小时前
1Panel 装好后 ufw 仍是 inactive:一次服务器安全盲区复盘
服务器·数据库·安全·网络安全·系统架构·实战·1panel
code2cat7 小时前
【随笔】MCP工具重试:任务已经创建,响应丢了怎么办
数据库·mcp
wuyk5559 小时前
Spider-第10讲-SQLite持久化与去重
数据库·爬虫·sqlite
乌暮11 小时前
Spring AOP 与 Spring 事务:用大白话彻底理解原理与实践
java·数据库·spring boot·后端·sql·学习·spring
小此方11 小时前
Re:Mysql数据库基础篇(十二):什么是事务、事务解决了什么问题、事务的四大属性、事务的操作方式
数据库·mysql
flash俊杰13 小时前
v0.5-01 让桌面 AI 助手记得住:预算组装、递归摘要、长期记忆与评估基线
数据库·安全·架构