postgresql 内核源码分析 btree索引插入分析,索引页面分裂流程,多举措进行并发优化,对异常进行保护处理

这篇具有很好参考价值的文章主要介绍了postgresql 内核源码分析 btree索引插入分析,索引页面分裂流程,多举措进行并发优化,对异常进行保护处理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Btree索引插入流程分析

专栏内容

  • postgresql内核源码分析
  • 手写数据库toadb
  • 并发编程

开源贡献

  • toadb开源库

个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

前言

B树索引在PostgreSQL中得到了广泛应用,它是一种自平衡树数据结构,可以维护有序数据并允许进行搜索、顺序访问、插入和删除操作。在PostgreSQL中,可以在任何数据类型上使用B树索引,支持排序,支持大于、小于、等于、大于或等于、小于或等于的搜索。

B树具有一些重要的特征。首先,B树是平衡的,每个叶子页与根都由相同数量的内部页分隔开,因此搜索任何值都需要花费相同的时间。其次,B树是多分支的,每个页面通常包含许多(数百个)ctid,因此B树的深度很小,对于非常大的表,实际上可以达到4-5的深度。最后,索引中的数据按非递减顺序存储(在页面之间和每个页面内部),并且同一级别的页面通过双向列表相互连接,因此不需要每次都返回到根,可以通过遍历链表获取一个有序的数据集。

PostgreSQL中的B树索引是一种高效的数据结构,可以用于加速对有序数据的搜索和访问。通过使用B树索引,可以大大提高数据库的性能和响应时间。

概述

本文主要介绍postgresql 中常用的索引类型btree的插入过程,通过本文对postgresql 索引查询的代码有一定了解,希望能够帮助基于postgresql做内核开发的同学,当然理解也有限,正在看这部分的同学可以在评论区一起来探讨。

插入流程

在进行SQL语句 insert一条数据行时,如果某一列带有索引,那么在插入数据的同时,也需要插入一条索引项;

索引项中需要记录数据行的tid,也就是位置信息,所以插入索引的时机,是在插入数据行成功后,使用数据行的位置信息生成一条索引项,然后进行索引项的插入流程;

索引项的插入整体流程如下:

  • 生成新的索引项;
  • 检查唯一性;
  • 查找合适的插入位置;
  • 对待插入索引页面检查空闲空间是否足够;
  • 如果空间不足,则将当前索引页面进行分裂为左右两半;
  • 然后将新索引项加入左或右页面;

准备阶段

生成一个索引项,这就是需要插入的新索引项;

先是遍历btree树,从root中的元组进行二分查找,找到该索引项对应的范围所在的下层的节点的pageno,如果层级较多,每次都是如此,最终找到叶子节点,找到要插入的叶子节点的索引数据块;

检查唯一性

对于主键的列,需要检查索引的唯一性,避免插入两条相同的索引;

对于主键肯定是需要唯一的存在,为什么要在这里做唯一性检查,而不是在数据插入时检查呢?

因为在插入数据时,如果检查唯一性,还是要通过索引扫描来比对,所以直接放到索引插入阶段,当索引检查不通过时,那么前面插入的数据也是无效的。

  • 检查时,在索引树中先找到相同值的位置;
  • 然后对比它的tid是否一样,如果不一样,也就是除了之前新插入数据之外还有一条相同字段的数据存在;
  • 检查相同索引项对应的数据行的事务是否提交; 如果没有提交,则等待该事务结束,再进行检查;
  • 如果相同索引项对应的数据行的事务已经提交,那么就产生了冲突;
  • 当有冲突时,就会报错,abort当前事务,也就会导致前面插入的数据无效;

查找合适的插入位置

在前一步已经找到了符合的叶子节点,在 _bt_findinsertloc 中,还需要进一步查找合适的位置,主要有以下几种情况:

  • 对于非heapkeyspace的索引,相同键值索引项,需要插入到最右边;此时要向右查找节点;
  • 对于多个索引页面都有相同highkey,也就是重复值,那么这些页面都适合插入新值,找一个空间足够的即可;
  • 还有一种情况是,对于数据多版本需要跨页时,要新增索引项,此时索引项的值是不变的,只是对应的tid发生了变化;对于这种情况,可以先检查删除的dead索项,避免索引的分裂;

在上面向右查找的情况,postgresql做了一些优化,综合了向右找的代价,和提前分裂的代价,有概率提前分裂;

对于新插入的索引项,不能大于空闲空间的三分之一;

检查空闲空间

在 _bt_insertonpg 调用中,实现了剩余空间检查,页面分裂,索引项的真正插入;

当前查找到索引页空间小于当前新索引项时,就需要进行索引页的分裂;
如果空间足够,则直接插入即可;

页面分裂

Btree的页面分裂是插入环节中的关键点,也是难点所在;在这个环节中,因为会修改多个节点页,对并发访问影响比较大,postgresql在这里做了一些优化;

我们先按常规思路来模拟一下分裂的过程;
某一节点分裂成左右两个节点,将旧节点的内容平分到新节点中,然后变更左节点的后续链接,变更右节点的前续和后续链接,将它中加入本层的双向链表,最后将新增节点信息加到父节点中;

这样一个分裂的过程中,所有变更节点都需要加互斥锁。在btree查找章节中已经介绍过,上下层之间是单向的,也就是只能从父节点找到下层节点,为了避免重复查找父节点,在确定插入节点时,就已经将路的层次路径记了下来;

下面我们来看postgresql中如何进行分裂,以及进行了那些地方的优化;

按分裂的节点类型,分为根节点,页子节点,中间branch节点的分裂;

根页面的分裂

在一开始,数据不多时,根页面节点就足够存下了,此时根节点也是叶子节点;随着数据的增多,根节点就需要分裂了。

它的分裂不同于其它类型,下面图示分析:

postgresql 内核源码分析 btree索引插入分析,索引页面分裂流程,多举措进行并发优化,对异常进行保护处理,postgresql,#  postgresql内核源码分析,postgresql,数据库

当root节点分裂时:

1.节点分裂

  • 先加锁待分裂的root节点;
  • 在本地内存中生成一个left节点,将它的btpo_flags设置为分裂中,并且插入highkey,也就是右节点的最小值;
  • 在索引文件中生成right节点,注意这里是直接在索引文件中增加一个节点,同时加互斥锁;设置right节点的前继为待分裂的root节点,后继为空,因为它是最右的节点;
  • 将旧root中的数据根据划分位置,分别移动到左右两个节点中;并且将待插入值插入合适位置,因为是分裂,所以一定是有空闲空间的,所以在此处直接插入;
  • 将left节点拷到待分裂节点中;这样,left,right节点都在索引文件中了;
  • 将left,right页面标脏,同时更新待分裂节点的右节点的前继为right节点,并生成分裂的WAL;
  • 解锁待分裂节点的右节点;

2.更新父节点

  • 然后新创建一个root节点,将左右节点的信息插入新的root节点中;
  • 将left节点的正在分裂标志取消;
  • 新root节点页面标脏,以及生成新root页面修改的WAL;
  • 解锁root,left,right节点;

在通过pginspect插件看时,就会发现root节点的pageno随着数据增加会一直变动,其实就是树的层次在增加,每增加一层级时,就会新创建一个root页面;

叶子页面分裂

页子节点页面的分裂,是最常见的一种,当然也经过了很多的优化,比如对于相同键值的数据;还有对于多版本跨节点存储时,会再插入一条索引项,还有对于NULL值的存储;

先来看一下叶子页面分裂的整体流程示意图:

postgresql 内核源码分析 btree索引插入分析,索引页面分裂流程,多举措进行并发优化,对异常进行保护处理,postgresql,#  postgresql内核源码分析,postgresql,数据库

叶子节点分裂如下:

1.节点分裂

  • 加锁待分裂节点;
  • 在本地内存中生成一个left节点,将它的btpo_flags设置为分裂中,并且插入highkey,也就是右节点的最小值;
  • 在索引文件中生成right节点,注意这里是直接在索引文件中增加一个节点,同时加互斥锁;设置right节点的前继为待分裂的root节点,后继为空,因为它是最右的节点;
  • 将旧root中的数据根据划分位置,分别移动到左右两个节点中;并且将待插入值插入合适位置,因为是分裂,所以一定是有空闲空间的,所以在此处直接插入;
  • 将left节点拷到待分裂节点中;这样,left,right节点都在索引文件中了;
  • 将left,right页面标脏,同时更新待分裂节点的右节点的前继为right节点,并生成分裂的WAL;
  • 解锁待分裂节点的右节点;

2.更新父节点

  • 加锁待分裂节点的父节点,然后解锁右节点;这一步解锁也很关键,优化了并发性能;
  • 将右节点信息,也就是左节点的highkey值, 插入父节点中; 当然这一步可能会递归进行,因为父节点有可能还会进行分裂,直到root再次分裂,就会增加树的层次;
  • 生成父节点变动的WAL;
  • 解锁父节点,left节点;

branch页面分裂

branch节点,是一种btree中间层的节点类型,它们存储的都是下层索引节点的页面位置和对应页面上的最小值;只有叶子节点上才会存储数据页的信息;

branch节点的分裂,类似于叶子节点的分裂;

只是在更新父节点时,多了一步,将left节点的正在分裂标志取消,也就是对于中间层的分裂已经完成;

新索引项加入

如果不发生分裂,那么插入的流程如下:

  • 找到索引插入位置;
  • 索引项插入页面,并给当前页面置脏标记;
  • 更新meta页的fastroot;如果树的某一层只有一个节点时,这个节点就是fastroot,记录到meta页面,下次遍历时,直接从fastroot开始扫描;
  • 检查是否需要记录fastpath对应的块,也就是叶子节点最右节点;

索引页面插入

因为btree索引是一种有序的索引,也是存储有序的,所以增加一条索引项时,如果在插入位置没有空槽位,就需要将当前位置及后面的索引项,向后移一个槽位,再将新索引项插入;

并发控制

在整个插入过程中,查找过程,还有分裂时持有多个块的情况进行了优化;

主要通过以下措施:

扫描和插入时

这两个过程中,只会对当前页面加锁;
在索引扫描时,结束一个索引页面就会释放,再加下一个索引页面加锁,这样保持了很好的并发访问性能;
索引项只记录向下的关系,所以在扫描过程中,会记录父子关系的stack,这方便在分裂时,向上递归;

索引分裂

在分裂的时候,会加多个节点的锁,加锁原则是从左到右,避免死锁发生;

分裂时减少了加锁的节点数量,从叶子节点开始,叶子页面分裂时,会加左节点和右节点的锁;当更新父节点时,加锁父节点后,就释放了右节点的锁;整个过程中持有的锁,可能最多就是三个节点,当然还有短暂持有的锁,如meta节点,还有待分裂节点的右节点的锁,最多时会有四个节点;

fastpath

记录叶子层的rightmost,对于null直接插入还有批量顺序插入时,直接就可以从fastpath找到插入节点,也就是叶子节点的最右节点;如果不符合时,再遍历查找;
这里使用了条件锁,得不到时,也会进行遍历查找,增加并发访问性能;

fastroot

当树的某一层只有一个节点时,那这个节点就是fastroot,不用从root进行遍历,而从fastroot遍历就可以,加快速度;

对于异常的保护

因为索引页面不像数据页面是多版本机制,为了保持索引的存储精炼,而采用了原地更新,这就需要在更新时,如果服务异外宕机了,数据还能保持一致性和完整性;

在插入时的保护

如果没有发生页面分裂,在插入数据时发生了异常,此时是由WAL来恢复;WAL记录了插入的位置,以及原有数据位置的变化;

在分裂时的保护

首先在分裂时,开始只将右节点加入了索引文件,分裂节点的数据并没有发生变化;此时异常并不会影响;

接下来,分裂节点数据发生变化,此时它的flag还是正在分裂中,那么数据其实是完整的,分别在左右两个页面上,只是从父节上只能找到左节点,从左节点再顺序通过后继链表就可以找到右节点,这在前面btree索引查找时就分享过。

结尾

非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!文章来源地址https://www.toymoban.com/news/detail-717532.html

到了这里,关于postgresql 内核源码分析 btree索引插入分析,索引页面分裂流程,多举措进行并发优化,对异常进行保护处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • postgresql 内核源码分析 事务提交回滚状态记录 clog机制流程,commit log文件格式,事务状态为什么单独记录的原因,分组优化及leader更新机制

    ​ 专栏内容 : postgresql内核源码分析 手写数据库toadb 并发编程 ​ 开源贡献 : toadb开源库 个人主页 :我的主页 管理社区 :开源数据库 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. PostgreSQL是一种开源的关系型数据库管理系统,其内核源码的分析对于深入理

    2024年02月08日
    浏览(54)
  • 【PostgreSQL内核学习(一)—— Ubuntu源码安装PostgreSQL】

    下载地址:https://www.postgresql.org/ftp/source/v10.1/ 执行命令: 解压成功后显示: 出现问题: 解决方法:执行以下命令。 执行命令: 注意:如果希望后续在gdb时可以查看代码,则需要添加–enable-debug make时出现错误: 解决方法:找到 copy_fetch.c 文件。 文件路径如下: /home/jia/pg

    2024年02月16日
    浏览(59)
  • 【PostgreSQL内核学习(二)—— 查询分析】

    声明 :本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。 本文主要参考了《PostgresSQL数据库内核分析》一书   在PostgreSQL中, 查询处理 是指 处理和执行SQL查询语句的整个过

    2024年02月17日
    浏览(58)
  • postgresql内核分析 spinlock与lwlock原理与实现机制

    ​ 专栏内容 : postgresql内核源码分析 手写数据库toadb 并发编程 个人主页 :我的主页 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. ======================================== 在postgresql 中,有大量的并发同步,所以避免不了使用很多保护锁。 同时为了提升并发的性能,

    2024年02月13日
    浏览(47)
  • mysql一两种索引方式hash和btree

    索引是帮助mysql获取数据的数据结构。最常见的索引是Btree索引和Hash索引。 不同的引擎对于索引有不同的支持:Innodb和MyISAM默认的索引是Btree索引;而Mermory默认的索引是Hash索引。 Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根

    2024年02月16日
    浏览(40)
  • 【PostgreSQL在线创建索引(CIC)功能的锁分析以及使用注意】

    前一篇文章提到了普通创建索引会阻塞DML操作 PostgreSQL创建索引的锁分析和使用注意 而PostgreSQL里可以使用create index concurrently 在线创建索引(CIC)功能,降低创建索引在表上申请的锁的级别,ShareUpdateExclusiveLock级别的锁和RowExclusiveLock不冲突,不会阻塞表上的DML操作。 1.1 在线创

    2024年02月01日
    浏览(52)
  • 【从删库到跑路】MySQL数据库的索引(一)——索引的结构(BTree B+Tree Hash),语法等

    🎊专栏【MySQL】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 🥰欢迎并且感谢大家指出小吉的问题 索引(index)是帮助MySQL 高效获取数据 的 有序 的 数据结构 在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方

    2024年02月16日
    浏览(55)
  • sql索引分析-插入了 a、b、c、d 四个字段作为索引,只要带上了a,那么任何排列的组合,都可以走索引。

    1、如果创建了一个索引   ALTER TABLE `table_A` ADD KEY `nid_sn_key`(`a`,`b`,`c`,`d`) USING BTREE; 第一种情况: explain SELECT * FROM `table_A` WHERE `a` = \\\"xxx\\\"; explain SELECT * FROM `table_A` WHERE `a` != \\\"xxx\\\"; 会走索引 第二种情况: explain SELECT * FROM `table_A` WHERE `b` = \\\"xxx\\\"; 不走索引 等等,如果单独查询c,

    2024年02月09日
    浏览(39)
  • Linux内核源码分析 (A)常见内核面试题

    系统调用 do_fork() : copy_process() 定时中断 do_timer() 唤醒进程 wake_up_process() :进程由睡眠状态转为 RUNNING 状态 系统调用 sys_sched_yield() 改变进程的调度策略 sched_setscheduler() : 什么情况下会发生调度时机:阻塞操作、中断返回之前(系统调用返回用户空间时)、被唤醒的进程会

    2024年02月10日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包