postgres源码解析48 Btree节点分裂点确认流程--1

news2024/12/25 12:49:51

由于Btree数据结构特性,当节点达到上溢条件时会发生分裂,进而保持Btree的原本特性 B树 详解及C语言简单实现,在之前的postgres 源码解析 45 btree分裂流程_bt_split已对分裂流程进行讲解,接下来将从源码角度学习postgres btree分裂点的确认流程,本篇侧重将其数据结构和分裂规则相关知识,后续呈现执行流程及其实现原理。

关键数据结构

在这里插入图片描述

说明:
在计算分裂点的整个过程中,会生成FindSplitData结构体,该结构体记录了寻找分裂位置的相关信息,特别是最佳分裂为止以及待插入元组信息。
在这里插入图片描述
分裂最优点的确定会在众多的候选分裂点中选取,分裂点的数据结构由SplitPoint表示,该结构体记录了左页和右页的空闲空间,落在右叶中第一个元组在分裂页中的偏移量以及插入元组是否插入原分裂页等信息。
在这里插入图片描述

填充因子的确认

pg会根据页节点类型与元组情况确认分裂页的填充因子,具体情况如下:
1 分裂页为非叶子结点 ,填充因子为70 %;
2 分裂页为该层的最右侧节点 ,填充因子为90 %;
3 非最右页叶子结点 , 90 %或者 50 %,具体情况具体分析,如下:
对于非最右页叶子结点填充因子的确认设计到一种优化,其定义在 _bt_afternewitemoff 函数
其规定需满足以下条件:
1) 该索引为复合索引;
2) 插入的索引元组在后续分裂后非索引页的第一个元组
3)元组拥有固定大小
4) 元祖宽度不超过16字节

这种优化适用于当复合索引中存在局部单调增加插入的模式,且领先的键属性值形成局部分组,并且我们预计接下来会再次插入相同的分组时

讨论
1 插入元组为最右端
在这里插入图片描述

1)如果插入元组与分裂页最大项指针对应的索引元组相等的属性数目小于复合索引的属性总数(至少含有一个属性对应的数值相等),规定此时填充因子为 90 %;
2)反之填充因此为 50 %;

这么做是因为,当满足了上述所有条件后,pg推测如果下面还会有插入操作,下一个插入的大概率还会是同类型的键值递增的元组(比如此例中 id依旧为1,key为H的元组),且这个元组会被插入在分裂后的右页上,这时如果这次分裂用了填充百分比计算分裂点的delta的话,右页会有更多的可用空间,会延缓右页的再分裂。

2 插入元组非最右端
1) 如果Curr索引元组指向的堆表块号 Curr_heap_block_no 与Prev索引元组指向的堆表块号Prev_heap_block_no 相同 || Curr_heap_block_no = Prev_heap_block_no+1 且Curr索引元组指向的堆元组为堆页的第一个元组:
 a. 如果Curr索引元组与Prev索引元组相等的属性数量在 [ 1,索引属性总数),进一步判断插入元组处于分裂页何处,
   若处于分裂页空间的 90 % 位置之前,填充因子为50 %;
在这里插入图片描述
若处于分裂页空间的 90 % 位置之后,填充因子为90 %;
在这里插入图片描述

2)其他情况的非最右页叶子结点 填充因子为50 %;
需要注意的是,这项优化不是百分百精确的,上述所满足的条件都是用来推断“局部单调增加插入,且领先的键属性值形成局部分组,并且预计接下来会再次插入相同的分组”这个场景的发生,但推断不一定对,可能会出现一些误优化,但误优化的结果并不致命,平均下来分裂点在页面的中间点。即优化对了,增加效率,优化错了,不会降低效率。源码参考_bt_afternewitemoff函数。

分裂间隔的确定

什么是分裂间隔?
根据“Prefix B-Trees”论文中的描述,分裂间隔只是在待分裂页的中间节点周围确定一个范围,在这个范围中找到一个分裂点,使得最终插入父节点的分隔符最短。
可以看出分裂间隔是以最佳均衡空间分裂点为中心的。pg加入了填充百分比计算delta后,使最佳均衡空间分裂点split[0]不再一定是页面中间点。这里只是设置初始间隔,不同策略下的分割间隔不同。
叶子结点与非叶子结点的容忍值是不同的,pg中规定:

	/*
	 * Determine leftfree and rightfree values that are higher and lower than
	 * we're willing to tolerate.  Note that the final split interval will be
	 * about 10% of nsplits in the common case where all non-pivot tuples
	 * (data items) from a leaf page are uniformly sized.  We're a bit more
	 * aggressive when splitting internal pages.
	 *  LEAF_SPLIT_DISTANCE = 0.05     INTERNAL_SPLIT_DISTANCE = 0.075
	 */
	if (state->is_leaf)
		tolerance = state->olddataitemstotal * LEAF_SPLIT_DISTANCE;   
	else
		tolerance = state->olddataitemstotal * INTERNAL_SPLIT_DISTANCE;

以 splits[0]为基准,确认左右页的空闲空间界限,找到第一个不在此临界范围内的分裂点序号,以此作为初始间隔值。

	/* First candidate split point is the most evenly balanced */
	spaceoptimal = state->splits;
	lowleftfree = spaceoptimal->leftfree - tolerance;
	lowrightfree = spaceoptimal->rightfree - tolerance;
	highleftfree = spaceoptimal->leftfree + tolerance;
	highrightfree = spaceoptimal->rightfree + tolerance;

	/*
	 * Iterate through split points, starting from the split immediately after
	 * 'spaceoptimal'.  Find the first split point that divides free space so
	 * unevenly that including it in the split interval would be unacceptable.
	 */
	for (int i = 1; i < state->nsplits; i++)
	{
		SplitPoint *split = state->splits + i;

		/* Cannot use curdelta here, since its value is often weighted */
		if (split->leftfree < lowleftfree || split->rightfree < lowrightfree ||
			split->leftfree > highleftfree || split->rightfree > highrightfree)
			return i;
	}

perfectpenalty

含义:分裂间隔内的所有分裂点能够产生的最小的策略罚分。
作用:可以提高_bt_bestsplitloc寻找最佳分裂点的效率。

如何理解它的含义和作用?
perfectpenalty字面上看是完美罚分的意思,这里我们称它为最优策略罚分。
流程上,它作为_bt_strategy的返回值,返回给_bt_bestsplitloc函数当做最后挑选最佳分裂点的参照。
_bt_bestsplitloc会调用_bt_split_penalty对每个候选分裂点都计算一个罚分penalty,计算出penalty会先和perfectpenalty做一个比较,如果小于等于perfectpenalty的话,就不用再计算下一个分裂点,而是直接将此分裂点作为最佳分裂点返回,所以perfectpenalty起到了提前结束循环遍历的作用,提高了_bt_bestsplitloc的效率。如果penalty一直都比perfectpenalty大,则返回它们中最小的那一个,那这样就需要遍历分裂间隔内所有的分裂点。

各场景介绍
了解了策略和perfectpenalty的概念,下面分场景介绍策略的选择流程:
1 叶子结点 – 一般情况
策略:最一般的情况
结果:保持原有的分裂间隔,保持原有的splits顺序

perfectpenalty为分裂间隔边界元组的最小区分键值数。所以在分裂间隔内是可以找到一个分裂点可以达到最佳后缀截断效果的。下图为perfectpenalty产生过程:
在这里插入图片描述

2 叶子结点–存在大量重复元组
策略:SPLIT_MANY_DUPLICATES
特点:此场景下,页中元组有很多重复元组,但不全是单一的重复元组。
结果:将分裂间隔设置为所有分裂点个数,保持原有splits顺序。

场景确定过程:
_bt_strategy函数先调用_bt_keep_natts_fast计算了分裂间隔边界的元组的最小区分键值数,发现大于总键值数(已有键值都区分不了返回总键值数+1),即没有差异。因为元组是逻辑递增的,所以说明分裂间隔内都是重复元组。这时需要再确认一下分裂间隔外是否也都是一样的重复元祖,再为页面两端的元组调用一次_bt_keep_natts_fast,看它们是否有差异,结果发现它们最小区分键值数a不大于总键值数,即有差异。则说明,页面在分裂间隔外存在差异元祖,且最小区分键值数为a。
PG给此场景下的页面分裂,使用SPLIT_MANY_DUPLICATES策略。但并没有取a为perfectpenalty,而是直接采用的总键值数为perfectpenalty这是为了让页面尽量在重复元组块的边界分裂(防止可能会出现的连续不平衡的分裂)。
为什么以总键值数为perfectpenalty能够让页面在重复元祖块边界分裂?
因为在_bt_bestsplitloc中,重复元组块中的分裂点的penalty是总键值数+1,所以值为总键值数的perfectpenalty,会在重复块的边界结束最佳分裂点的查找。参考流程图解第7步和下图:

3.叶子节点-全部为重复元祖

1)该页为最右页
策略:SPLIT_SINGLE_VALUE
特点:页中全部都是一样的元组,且页面为当前层最右边的页面。
结果:将分裂间隔设置为1,改变填充百分比为96%,调用_bt_deltasortsplits 重新排序 splits 数组。

这时_bt_strategy给页面返回的perfectpenalty是总键值数+1,其实这里perfectpenalty是多少已经起不到什么作用了,因为分裂间隔只有1,最后_bt_bestsplitloc只会返回用96%的填充百分比重新排序的splits[0],由于填充百分比很大,这个最佳分裂点会非常靠右,分裂出的右页将会有大量可用空间,而左页几乎是满的。

2)该页非最右页

① 该页为重复项结束页
策略:SPLIT_SINGLE_VALUE
特点:页中全部都是一样的元组,页面不是当前层最右边的页面,且右兄弟节点中没有 该重复项,即为重复项的结束页。
结果:将分裂间隔设置为1,改变填充百分比为96%,调用_bt_deltasortsplits 重新排序 splits 数组。

跟上一种场景唯一的区别在于perfectpenalty为插入项和页面highkey的最小区分键值数。由于分裂间隔设置为1,perfectpenalty是多少也起不到什么作用。最终都是返回splits[0]。

如何判定页面是不是重复项的结束页?
通过看插入项和页面highkey是否有区别,因为highkey是右页面的最小值,且页中元组键值递增,如果有区别,说明下一页将不会含有该重复项,否则下一页还是以该重复项开头。

② 该页非重复项结束页
策略:SPLIT_DEF AULT
特点:页中全部都是一样的元组,页面不是当前层最右边的页面,且右兄弟节点中有 该重复项,即不为重复项的结束页。
结果:保持原有分裂间隔,保持原有splits顺序。

对于此场景下为什么不使用SPLIT_SINGLE_VALUE策略,源码中的解释是:

	/*
	 * Single value strategy is only appropriate with ever-increasing heap
	 * TIDs; otherwise, original default strategy split should proceed to
	 * avoid pathological performance.  Use page high key to infer if this is
	 * the rightmost page among pages that store the same duplicate value.
	 * This should not prevent insertions of heap TIDs that are slightly out
	 * of order from using single value strategy, since that's expected with
	 * concurrent inserters of the same duplicate value.

即,SPLIT_SINGLE_VALUE策略仅适用于不断增长的堆TID, 否则,应继续执行原始的默认策略拆分以避免病理性能…

4.中间节点
策略:SPLIT_DEFAULT
特点:所有中间节点的分裂都是默认策略
结果:保持原有分裂间隔,保持原有splits顺序。

_bt_findsplitloc流程图 (下篇讲解)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194715.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

揭密Realtek 致命漏洞:超过 1 亿次尝试破解物联网设备

国际知名白帽黑客、东方联盟创始人郭盛华警告说&#xff0c;自 2022 年 8 月开始&#xff0c;利用 Realtek Jungle SDK 中现已修补的关键远程代码执行漏洞进行攻击的攻击企图激增。 据郭盛华透露&#xff0c;截至 2022 年 12 月&#xff0c;正在进行的活动据称已记录了 1.34 亿…

前端sdk - 埋点

目录前端sdk 之小满np安装01 搭建环境01-项目目录01-2 依赖包01-3 rollup.config.js01-4 tsconfig.json 28行01-5 package.json01-6 src / core / index.ts01-7打包效果02 初始化 Tracher02-1 core / index.ts02-2 types/ index.ts03 重写history事件 监听history | hash 路由等…

【Spring Cloud Alibaba】(一)微服务介绍 及 Nacos注册中心实战

文章目录前言I、微服务与Spring CloudII、Nacos 注册中心III、Spring Cloud Alibaba Nacos 实战1、新建父工程2、新建demo-a 服务3、新建 demo-b 服务4、实现服务调用&#xff1a;传统方式5、实现服务调用&#xff1a;NacosRibbon方式总结最后前言 Spring Cloud Alibaba微服务…

JS 设计模式(2)-- 复习

目录 策列模式 代理模式 观察者模式 发布订阅模式 模块模式 策列模式 策略模式定义了一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使他们可以相互替换&#xff0c;且算法的变化不会影响使用算法的用户&#xff0c;策列模式属于对象行为模式&#xff0c;它通过…

Java面试题(自用-持续更新)

本文目录如下&#xff1a;Java面试题一、基础知识JDK 和 JRE 有什么区别&#xff1f;String 属于基础的数据类型吗&#xff1f;基础类型有哪些?String str"xqz"与 String strnew String("xqz")一样吗&#xff1f;java 中操作字符串都有哪些类&#xff1f;…

王佩丰 Excel 基础24讲 | 学习笔记(全)

第一讲&#xff1a;认识Excel 1.简介 excel能做什么&#xff1f; 数据存储 → 数据处理 → 数据分析 → 数据呈现 excel界面 补充&#xff1a;Excel数据分析步骤 ①提出问题&#xff1a;明确自己需要通过数据分析解决什么问题 ②理解数据&#xff1a;理解数据各个字段的含义…

【手写 Promise 源码】第十五篇 - 了解 generator 生成器

一&#xff0c;前言 上一篇&#xff0c;实现了 promisify 和应用场景介绍&#xff0c;主要涉及以下几个点&#xff1a; promisify 简介和测试&#xff1b;promisify 功能的实现&#xff1a;promisify、promisifyAll&#xff1b; 目前&#xff0c;Promise 部分已基本完成&…

FPGA实现图像任意位置显示,串口协议控制显示位置,提供工程源码和技术支持

目录1、图像任意位置显示理论基础2、设计思路和架构3、OV5640图像采集4、图像DDR3三帧缓存5、图像任意位置输出显示6、串口协议控制显示位置7、vivado工程介绍8、上板调试验证9、福利&#xff1a;工程源码获取1、图像任意位置显示理论基础 图像任意位置显示指的是在显示屏上的…

Linux新手渣渣上路史

时至2022年&#xff0c;IT行业的迅速发展大家也有目共睹&#xff0c;IT行业在社会的发展中起着举足轻重的作用。其中一角Linux系统&#xff0c;从诞生到开源&#xff0c;再到现在受大众的欢迎&#xff0c;是一个很好的例子。Linux和windows类似&#xff0c;是一个操作系统&…

java 微服务高级之分布式事务 Seata框架 CAP定理 BASE理论 XA模式 AT模式 TCC模式 SAGA模式

分布式事务问题 1.1.本地事务 1.2.分布式事务 一旦有一个失败了&#xff0c;其他两个不知情失败的情况&#xff0c;还是执行并成功 在分布式系统下&#xff0c;一个业务跨越多个服务或数据源&#xff0c;每个服务都是一个分支事务&#xff0c;要保证所有分支事务最终状态一致…

【JavaEE】线程安全的集合类

引言 在Java标准库中&#xff0c;大部分集合类都是线程不安全的。Vector(比ArrayList多了同步化机制就变得线程安全了)&#xff1b;Stack(继承Vector)&#xff1b;Hashtable(只比Hashmap多了线程安全)&#xff1b;以Concurrent开头的集合类&#xff1a;ConcurrentHashMap、Con…

Echarts 用图形纹理来填充颜色(color - pattern)

第006个点击查看专栏目录在上一篇文章中已经讲过 ECharts线性渐变色示例演示&#xff08;2种渐变方式&#xff09;&#xff0c;这个示例的颜色使用纹理来做填充&#xff0c;纹理填充&#xff1a; pattern color:{ //纹理填充 image: patternImg, repeat: ‘repeat’ } 示例效果…

禾川HCQ ModBUS+485主从站调试

硬件&#xff0c;485转usb&#xff0c;如果主站是plc&#xff0c;不需要这个线&#xff0c;我现在主站是电脑&#xff0c;调试用。 HCQ0 禾川控制器。 软件 modbus tools 调试软件&#xff0c;自行下载吧&#xff0c;社区传不上去。 硬件连接时注意交叉连接&#xff0c;HCQ0 A端…

MATLAB 逻辑数组

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

Java⽇志框架学习笔记

目录 1.⽇志概述 1.1 ⽇志是⽤来做什么的&#xff1f; 1.2 为什么要⽤到⽇志框架&#xff1f; 1.3 现有的⽇志框架有哪些&#xff1f; 1.4 ⽇志⻔⾯技术 2.logback 2.1 logback介绍 2.1.1 logback 模块 2.1.2 logback 组件 2.1.3 logback 配置 2.1.4 logback.xml 配…

2023网络爬虫 -- 获取动态数据

一、网站的正常界面1、网址https://movie.douban.com/typerank?type_name%E5%8A%A8%E4%BD%9C%E7%89%87&type5&interval_id100:90&action2、正常的页面二、爬取数据1、源代码import requests头{"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64…

首屏加载速度慢怎么解决?

一、什么是首屏加载 首屏时间&#xff08;First Contentful Paint&#xff09;&#xff0c;指的是浏览器从响应用户输入网址地址&#xff0c;到首屏内容渲染完成的时间&#xff0c;此时整个网页不一定要全部渲染完成&#xff0c;但需要展示当前视窗需要的内容 首屏加载可以说是…

分享156个ASP源码,总有一款适合您

ASP源码 分享156个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 156个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1Mc-zWjUyk9Lx8TXv5cvZTg?pwds2qi 提取码&#x…

Office 365用户登录审核

黑客访问端点设备&#xff0c;以窃取公司特定数据、员工个人数据或任何其他可能对他们有任何用处的有价值的信息。 为了帮助您防止这种攻击&#xff0c;我们编写了一个参数列表&#xff0c;可以帮助您识别异常日志&#xff0c;这通常是攻击的第一个标志。异常登录活动是安全漏洞…

Vue使用ElementUI的确认框进行删除操作(包含前后端代码)

前言 今天做自己项目的时候&#xff0c;有一个删除的业务&#xff0c;正好遇到了确认框&#xff0c;在此纪念一下。 这里我是使用的ElementUI的确认框&#xff01; 首先ElementUI的确认框是这么说明的&#xff1a; 从场景上说&#xff0c;MessageBox 的作用是美化系统自带的 …