当前位置: 首页 > ops >正文

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 不会为该附加表创建新索引,而是直接完成附加操作。
http://www.xdnf.cn/news/19777.html

相关文章:

  • 分布式AI算力系统番外篇-----超体的现实《星核》
  • Vue基础知识-重要的内置关系:vc实例.__proto__.__proto__ === Vue.prototype
  • 股指期货可以通过移仓长线持有吗?
  • AppInventor2 如何自定义包名?
  • 华为云云原生架构赋能:大腾智能加速业务创新步伐
  • 【深度学习新浪潮】视觉大模型在预训练方面有哪些关键进展?
  • 鸿蒙UI开发实战:解决布局错乱与响应异常
  • 企业实用——MySQL的备份详解
  • 基于机器学习的Backtrader波动性预测与管理
  • Kubernetes ConfigMap 更新完整指南:原理、方法与最佳实践
  • PyTorch实战——ResNet与DenseNet详解
  • Huggingface终于没忍住,OpenCSG坚持开源开放
  • flume拓扑结构详解:从简单串联到复杂聚合的完整指南
  • Linux 的信号 和 Qt 的信号
  • IO_HW_9_3
  • MySQL数据库恢复步骤(基于全量备份和binlog)
  • 揭秘ArrowJava核心:IndexSorter高效排序设计
  • Cookie、Session、登录
  • 一个工业小白眼中的 IT/OT 融合真相:数字化工厂的第一课
  • SQL Server核心架构深度解析
  • AlexNet:计算机视觉的革命性之作
  • PostgreSQL性能调优-优化你的数据库服务器
  • JVM调优与常见参数(如 -Xms、-Xmx、-XX:+PrintGCDetails) 的必会知识点汇总
  • 【学Python自动化】 9.1 Python 与 Rust 类机制对比学习笔记
  • 【WPS】WPSPPT 快速抠背景
  • 通过SpringCloud Gateway实现API接口镜像请求(陪跑)网关功能
  • 进攻是最好的防守 在人生哲学中的应用
  • 百度智能云「智能集锦」自动生成短剧解说,三步实现专业级素材生产
  • 以太坊网络
  • Spring Boot中MyBatis Plus的LambdaQueryWrapper查询异常排查与解决