PostgreSQL 中唯一索引的工作原理

引言

在研究分区表相关解决方案时,曾聚焦于如何在包含多个子表的分区表内,实现跨分区的数据唯一性保障。这类功能有时被称为全局索引,相关领域的讨论可参考邮件线程此处。尽管其中提出的思路具备一定合理性,但对应的实现方式却引发了较多争议 ------ 该方式改变了分区索引的存储逻辑,本质是将所有分区索引合并存储,并以TableOid作为内部引用的键值。

基于此,探索一种替代方案:在不改变 PostgreSQL 原有核心机制的前提下,实现跨分区唯一性保障。而要推进这一探索,首要任务是先厘清 PostgreSQL 分区表中,唯一性机制的底层工作原理。

CREATE INDEX 语句的唯一性保障

PostgreSQL 会通过以下基本流程检查唯一性冲突:

  1. 对目标子分区表执行堆扫描(heap scan)。
  2. 将可见元组(visible tuples)存储到一个BTSpool结构(记为 spool1)中,将无效元组(dead tuples)存储到另一个BTSpool结构(记为 spool2)中。因此,这里会用到两个BTSpool结构;若表中无无效元组,或无需保障唯一性,则 spool2 可能为 NULL。BTSpool结构可理解为索引元组的集合。
  3. 若 spool1 和 spool2 存在,则对其进行排序。
  4. 排序算法内置重复检测功能:若排序后有两个相同的元组连续出现,即判定为重复;若该索引要求唯一性,则会在此处抛出错误。
  5. 若排序过程中未检测到重复,PostgreSQL 会基于 spool1 和 spool2 构建索引树。
  6. 索引创建完成后,销毁所有BTSpool结构。
  7. 上述逻辑位于src/backend/access/nbtree/nbtsort.c文件的btbuild()函数中,并由indexcmds.c文件的DefineIndex函数调用。根据活跃子分区表的数量,DefineIndex会多次调用btbuild

INSERT 与 UPDATE 语句的唯一性保障

在规划器(planner)和优化器(optimizer)阶段,PostgreSQL 已确定新数据应插入或更新至哪个子分区表:

  1. PostgreSQL 首先将堆元组(heap tuple)插入目标堆关系(heap relation)。
  2. 随后调用src/backend/access/nbtree/nbtinsert.c文件中的_bt_doinsert()函数,尝试插入与该堆元组关联的新索引元组。
  3. 若索引要求进行唯一性检查,PostgreSQL 会根据新堆元组构建扫描键(scan key),并调用_bt_check_unique()函数从堆分区表中查询是否存在匹配的现有元组。
  4. 若当前子分区中未查询到匹配的堆元组,则无冲突。
  5. 若当前子分区中查询到匹配的堆元组,则需执行以下额外检查:
  6. 若查询到的元组尚未提交(例如,另一个后端进程仍在处理该元组且未提交),当前进程会在此处等待,直至该后端进程提交或回滚。
  7. 当该后端进程提交或回滚后,当前进程会再次查询同一元组。
  8. 若该后端进程回滚,则无法查询到重复元组,因此无冲突。
  9. 若该后端进程提交,则仍可查询到重复元组,因此存在潜在冲突。
  10. 在抛出错误前,PostgreSQL 会再执行一次检查:获取当前待插入堆元组的可见性状态。这是为了覆盖一种特殊场景------当当前后端进程尝试插入或更新数据时,另一个后端进程正在执行CREATE UNIQUE INDEX CONCURRENTLY(并发创建唯一索引)操作。
  11. 检查待插入的当前元组是否能从堆关系中查询到。
  12. 若能查询到,则当前元组仍处于可见状态,必然存在冲突。
  13. 若无法查询到,则当前元组已变为不可见状态,不视为冲突。
  14. 若未检测到冲突,则继续构建索引树。

上述逻辑主要位于src/backend/access/nbtree/nbtinsert.c文件的_bt_doinsert()_bt_check_unique()函数中。

ATTACH 操作的唯一性保障

待附加(ATTACH)的表可能已定义唯一索引(或非唯一索引),也可能完全没有索引。ATTACH 操作过程中存在两种潜在情况:

  • 向分区表附加无索引的表时,PostgreSQL 会自动为该附加表创建新索引,且索引参数与原分区表保持一致。目前,该索引的创建流程遵循2.0节中定义的步骤。
  • 向分区表附加已定义唯一索引的表(且该索引的键与分区表的全局唯一索引键相同)时,PostgreSQL 不会为该附加表创建新索引,而是直接完成附加操作。
相关推荐
+VX:Fegn08953 分钟前
计算机毕业设计|基于springboot + vue建筑材料管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
2301_800256116 分钟前
B+树:数据库的基石 R树:空间数据的索引专家 四叉树:空间划分的网格大师
数据结构·数据库·b树·机器学习·postgresql·r-tree
大厂技术总监下海28 分钟前
用户行为分析怎么做?ClickHouse + 嵌套数据结构,轻松处理复杂事件
大数据·数据结构·数据库
alonewolf_9938 分钟前
深入理解MySQL事务与锁机制:从原理到实践
android·数据库·mysql
oMcLin1 小时前
如何在Ubuntu 22.04 LTS上优化PostgreSQL 14集群,提升大数据查询的响应速度与稳定性?
大数据·ubuntu·postgresql
朝依飞1 小时前
fastapi+SQLModel + SQLAlchemy2.x+mysql
数据库·mysql·fastapi
3***g2051 小时前
redis连接服务
数据库·redis·bootstrap
m0_598177231 小时前
SQL 方法函数(1)
数据库
oMcLin1 小时前
如何在Oracle Linux 8.4上通过配置Oracle RAC集群,确保企业级数据库的高可用性与负载均衡?
linux·数据库·oracle
信创天地1 小时前
核心系统去 “O” 攻坚:信创数据库迁移的双轨运行与数据一致性保障方案
java·大数据·数据库·金融·架构·政务