postgres 源码解析49 Btree节点分裂点确认流程--2

news2024/11/28 12:47:07

上篇讲解了分裂的规则和填充策略等内容,而本文着重讲解postgres Btree分裂点确认流程,接口函数为 _bt_findsplitloc。相关知识点见回顾:postgres源码解析48 Btree节点分裂点确认流程–1

执行流程

_bt_findsplitloc
该函数的功能是确定该分裂页分裂点的确定,并返回落在新页中的第一个索引元组在分裂页中偏移量 offsetNumber
在这里插入图片描述
1 首先初始化分裂左页与右页空闲空间,如果分裂页不是最右页,则右叶需去除高键所占用空间;

	/* Total free space available on a btree page, after fixed overhead */
	leftspace = rightspace =
		PageGetPageSize(origpage) - SizeOfPageHeaderData -
		MAXALIGN(sizeof(BTPageOpaqueData));

	/* The right page will have the same high key as the old page */
	if (!P_RIGHTMOST(opaque))
	{
		itemid = PageGetItemId(origpage, P_HIKEY);
		rightspace -= (int) (MAXALIGN(ItemIdGetLength(itemid)) +
							 sizeof(ItemIdData));
	}

	/* Count up total space in data items before actually scanning 'em */
	olddataitemstotal = rightspace - (int) PageGetExactFreeSpace(origpage);
	leaffillfactor = BTGetFillFactor(rel);

2 初始化 FindSplitData 结构体并填充相关字段信息;

	/* Passed-in newitemsz is MAXALIGNED but does not include line pointer */
	newitemsz += sizeof(ItemIdData);
	state.rel = rel;
	state.origpage = origpage;
	state.newitem = newitem;
	state.newitemsz = newitemsz;
	state.is_leaf = P_ISLEAF(opaque);
	state.is_rightmost = P_RIGHTMOST(opaque);
	state.leftspace = leftspace;
	state.rightspace = rightspace;
	state.olddataitemstotal = olddataitemstotal;
	state.minfirstrightsz = SIZE_MAX;
	state.newitemoff = newitemoff;

	/* newitem cannot be a posting list item */
	Assert(!BTreeTupleIsPosting(newitem));

	/*
	 * nsplits should never exceed maxoff because there will be at most as
	 * many candidate split points as there are points _between_ tuples, once
	 * you imagine that the new item is already on the original page (the
	 * final number of splits may be slightly lower because not all points
	 * between tuples will be legal).
	 */
	state.maxsplits = maxoff;
	state.splits = palloc(sizeof(SplitPoint) * state.maxsplits);
	state.nsplits = 0;

3 遍历分裂页所有索引元组,调用 _bt_recsplitloc 找到所有满足条件的候选分裂点,该信息保存在 FindSplitData结构体中的 splits数组;

for (offnum = P_FIRSTDATAKEY(opaque);
		 offnum <= maxoff;
		 offnum = OffsetNumberNext(offnum))
	{
		Size		itemsz;

		itemid = PageGetItemId(origpage, offnum);
		itemsz = MAXALIGN(ItemIdGetLength(itemid)) + sizeof(ItemIdData);

		/*
		 * When item offset number is not newitemoff, neither side of the
		 * split can be newitem.  Record a split after the previous data item
		 * from original page, but before the current data item from original
		 * page. (_bt_recsplitloc() will reject the split when there are no
		 * previous items, which we rely on.)
		 */
		if (offnum < newitemoff)
			_bt_recsplitloc(&state, offnum, false, olddataitemstoleft, itemsz);
		else if (offnum > newitemoff)
			_bt_recsplitloc(&state, offnum, true, olddataitemstoleft, itemsz);
		else
		{
			/*
			 * Record a split after all "offnum < newitemoff" original page
			 * data items, but before newitem
			 */
			_bt_recsplitloc(&state, offnum, false, olddataitemstoleft, itemsz);

			/*
			 * Record a split after newitem, but before data item from
			 * original page at offset newitemoff/current offset
			 */
			_bt_recsplitloc(&state, offnum, true, olddataitemstoleft, itemsz);
		}

		olddataitemstoleft += itemsz;
	}

	/*
	 * Record a split after all original page data items, but before newitem.
	 * (Though only when it's possible that newitem will end up alone on new
	 * right page.)
	 */
	Assert(olddataitemstoleft == olddataitemstotal);
	if (newitemoff > maxoff)
		_bt_recsplitloc(&state, newitemoff, false, olddataitemstotal, 0);

4 根据分裂页类型以及元组特点确定填充因子;

if (!state.is_leaf)
	{
		/* fillfactormult only used on rightmost page */
		usemult = state.is_rightmost;
		fillfactormult = BTREE_NONLEAF_FILLFACTOR / 100.0;
	}
	else if (state.is_rightmost)
	{
		/* Rightmost leaf page --  fillfactormult always used */
		usemult = true;
		fillfactormult = leaffillfactor / 100.0;
	}
	else if (_bt_afternewitemoff(&state, maxoff, leaffillfactor, &usemult))
	{
		/*
		 * New item inserted at rightmost point among a localized grouping on
		 * a leaf page -- apply "split after new item" optimization, either by
		 * applying leaf fillfactor multiplier, or by choosing the exact split
		 * point that leaves newitem as lastleft. (usemult is set for us.)
		 */
		if (usemult)
		{
			/* fillfactormult should be set based on leaf fillfactor */
			fillfactormult = leaffillfactor / 100.0;
		}
		else
		{
			/* find precise split point after newitemoff */
			for (int i = 0; i < state.nsplits; i++)
			{
				SplitPoint *split = state.splits + i;

				if (split->newitemonleft &&
					newitemoff == split->firstrightoff)
				{
					pfree(state.splits);
					*newitemonleft = true;
					return newitemoff;
				}
			}

			/*
			 * Cannot legally split after newitemoff; proceed with split
			 * without using fillfactor multiplier.  This is defensive, and
			 * should never be needed in practice.
			 */
			fillfactormult = 0.50;
		}
	}
	else
	{
		/* Other leaf page.  50:50 page split. */
		usemult = false;
		/* fillfactormult not used, but be tidy */
		fillfactormult = 0.50;
	}

5 调用 _bt_strategy 函数确定分裂策略和”最佳罚分” perfectpenalty
1)如果分裂页为非叶子结点,直接将 state->minfirstrightsz 作为 perfectpenalty,该字段含义是如果按照候选分裂点分裂后在右叶中位于最小的第一个索引元组大小。
2) 根据默认分裂间隔确定分裂区间,即leftmost索引元组, rightmost索引元组;
3)调用_bt_keep_natts_fast 找到 第一个leftmost与roghtmost属性值不同的属性号(perfectpenalty);
4)如果 perfectpenalty小于等于索引属性数量,则返回 perfectpenalty;
5)条件4)不满足,则根据第一个候选分裂点和最后一个候选分裂点重新确定leftmost索引元组, rightmost索引元组(可以看出,上述2-4步骤是确认perfectpenalty的一种优化方式)。
6)确定此时的 perfectpenalty大小,如果小于等于索引属性数量,则将分裂策略和 perfectpenalty 分别设置为 SPLIT_MANY_DUPLICATES和索引属性数量;如果大于且该页处于非叶子层最右节点,则将分裂策略设置为 SPLIT_SINGLE_VALUE,如果上述都不满足比较分裂页高键与待插索引比较进一步确认 perfectpenalty。

/*
 * Subroutine to decide whether split should use default strategy/initial
 * split interval, or whether it should finish splitting the page using
 * alternative strategies (this is only possible with leaf pages).
 *
 * Caller uses alternative strategy (or sticks with default strategy) based
 * on how *strategy is set here.  Return value is "perfect penalty", which is
 * passed to _bt_bestsplitloc() as a final constraint on how far caller is
 * willing to go to avoid appending a heap TID when using the many duplicates
 * strategy (it also saves _bt_bestsplitloc() useless cycles).
 */
static int
_bt_strategy(FindSplitData *state, SplitPoint *leftpage,
			 SplitPoint *rightpage, FindSplitStrat *strategy)
{
	IndexTuple	leftmost,
				rightmost;
	SplitPoint *leftinterval,
			   *rightinterval;
	int			perfectpenalty;
	int			indnkeyatts = IndexRelationGetNumberOfKeyAttributes(state->rel);

	/* Assume that alternative strategy won't be used for now */
	*strategy = SPLIT_DEFAULT;

	/*
	 * Use smallest observed firstright item size for entire page (actually,
	 * entire imaginary version of page that includes newitem) as perfect
	 * penalty on internal pages.  This can save cycles in the common case
	 * where most or all splits (not just splits within interval) have
	 * firstright tuples that are the same size.
	 */
	if (!state->is_leaf)
		return state->minfirstrightsz;

	/*
	 * Use leftmost and rightmost tuples from leftmost and rightmost splits in
	 * current split interval
	 */
	_bt_interval_edges(state, &leftinterval, &rightinterval);
	leftmost = _bt_split_lastleft(state, leftinterval);
	rightmost = _bt_split_firstright(state, rightinterval);

	/*
	 * If initial split interval can produce a split point that will at least
	 * avoid appending a heap TID in new high key, we're done.  Finish split
	 * with default strategy and initial split interval.
	 */
	perfectpenalty = _bt_keep_natts_fast(state->rel, leftmost, rightmost);
	if (perfectpenalty <= indnkeyatts)
		return perfectpenalty;

	/*
	 * Work out how caller should finish split when even their "perfect"
	 * penalty for initial/default split interval indicates that the interval
	 * does not contain even a single split that avoids appending a heap TID.
	 *
	 * Use the leftmost split's lastleft tuple and the rightmost split's
	 * firstright tuple to assess every possible split.
	 */
	leftmost = _bt_split_lastleft(state, leftpage);
	rightmost = _bt_split_firstright(state, rightpage);

	/*
	 * If page (including new item) has many duplicates but is not entirely
	 * full of duplicates, a many duplicates strategy split will be performed.
	 * If page is entirely full of duplicates, a single value strategy split
	 * will be performed.
	 */
	perfectpenalty = _bt_keep_natts_fast(state->rel, leftmost, rightmost);
	if (perfectpenalty <= indnkeyatts)
	{
		*strategy = SPLIT_MANY_DUPLICATES;

		/*
		 * Many duplicates strategy should split at either side the group of
		 * duplicates that enclose the delta-optimal split point.  Return
		 * indnkeyatts rather than the true perfect penalty to make that
		 * happen.  (If perfectpenalty was returned here then low cardinality
		 * composite indexes could have continual unbalanced splits.)
		 *
		 * Note that caller won't go through with a many duplicates split in
		 * rare cases where it looks like there are ever-decreasing insertions
		 * to the immediate right of the split point.  This must happen just
		 * before a final decision is made, within _bt_bestsplitloc().
		 */
		return indnkeyatts;
	}

	/*
	 * 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.
	 */
	else if (state->is_rightmost)
		*strategy = SPLIT_SINGLE_VALUE;
	else
	{
		ItemId		itemid;
		IndexTuple	hikey;

		itemid = PageGetItemId(state->origpage, P_HIKEY);
		hikey = (IndexTuple) PageGetItem(state->origpage, itemid);
		perfectpenalty = _bt_keep_natts_fast(state->rel, hikey,
											 state->newitem);
		if (perfectpenalty <= indnkeyatts)
			*strategy = SPLIT_SINGLE_VALUE;
		else
		{
			/*
			 * Have caller finish split using default strategy, since page
			 * does not appear to be the rightmost page for duplicates of the
			 * value the page is filled with
			 */
		}
	}

	return perfectpenalty;
}

6 结合上述分裂策略、候选分裂点和 perfectpenalty信息,调用 _bt_bestsplitloc在所有候选分裂点中确定最佳分裂点
在这里插入图片描述

7 释放内存

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

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

相关文章

金三银四一线大厂常见Java面试题面试题总结 1000+ 面试题

Java 面试八股文有必要背吗&#xff1f; 我的回答是&#xff1a;很有必要。你可以讨厌这种模式&#xff0c;但你一定要去背&#xff0c;因为不背你就进不了大厂。现如今&#xff0c;Java 面试的本质就是八股文&#xff0c;把八股文面试题背好&#xff0c;面试才有可能表现好。…

2020年欧空局10米土地覆盖数据

土地覆盖数据是我们平时最常用的地理数据之一&#xff0c;土地覆盖数据的来源也有很多种&#xff0c;之前我们介绍共过两个的30米精度的土地覆盖数据&#xff0c;分别为GlobeLand30土地覆盖数据和CLCD土地覆盖数据&#xff0c;&#xff08;可查看之前推送的文章&#xff09;&am…

Linux:用户空间非法指针coredump简析

1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. 背景 本文分析基于 ARM32 架构&#xff0c;Linux-4.14 内核代码。 3. 问题分析 3.1 测试范例 void main(void) {*(int *)0 8; }运行程序会 …

Seata-Server分布式事务原理加源码 (七) - TCC事务模式

TCC事务模式 首先我们先来了解常规的TCC模式。 什么是TCC TCC 是分布式事务中的二阶段提交协议&#xff0c;它的全称为 Try-Confirm-Cancel&#xff0c;即资源预留&#xff08;Try&#xff09;、确认操作&#xff08;Confirm&#xff09;、取消操作&#xff08;Cancel&#…

CV——day77 简读论文:视频中交通标志的跟踪检测

视频中交通标志的跟踪检测Detection-by-tracking of traffic signs in videos1 Introduction3 Methods3.1 Faster R-CNN3.2 Proposed shortest-path approach3.3 Tractor-based method3.4 IoU-based method6 ConclusionsDetection-by-tracking of traffic signs in videos 视频…

除了ChatGPT,还能用什么计划管理软件提高效率?

最近一段时间&#xff0c;人工智能工具ChatGPT成为互联网科技圈的热门话题。正如当年的阿尔法狗给世界带来的震动一样&#xff0c;人们讨论的最多的就是&#xff1a;ai智能会不会取代人工&#xff0c;因为ai的效率太高了&#xff0c;但再智能&#xff0c;也有ai永远也取代不了的…

ROS2机器人编程简述humble-第四章-IMPROVED DETECTOR .4

ROS2之TF2小练习-颜色随机器人和障碍物直接距离变化ROS2之TF2小练习-有哪些bug找找看里面给出了&#xff1a;ROS2机器人编程简述humble-第四章-BASIC DETECTOR .3需要改进哪些地方呢&#xff1f;检测之后&#xff0c;距离不变了……如何变化&#xff1f;这个问题可以问chatgpt吗…

【Linux】TCP并发网络编程

多线程网络编程 上一节我们讲到&#xff0c;当我们的多个客户端区连接同一个服务端的时候就会出现问题&#xff0c;这是因为一个返回值只能接收一个客户端传输的消息&#xff0c;那么我们想要多个客户端同时链接服务端&#xff0c;我们就要有这样一个思路&#xff0c;发过来一…

金三银四?铜三铁四才对吧......

往年的金三银四&#xff0c;今年被戏称为“铜三铁四”。知名的大厂HR们都在不断的裁员&#xff0c;能被保住不被裁掉可能就万事大吉了&#xff0c;赛道越来越窄&#xff0c;都在预测未来计算机行业是不是下一个土木工程&#xff1f; 我也算是软件测试岗位的老鸟了&#xff0c;…

2.6 尚品汇 day13 二级路由 饿了么ui 表单使用以及验证(不完整)、上线后的跨域代理(nginx)

二级路由 1.1路由结构 1.2 引入二级路由 1.2.2配置路由信息 1.3 声明导航 1.4配置路由出口 饿了么ui 表单使用以及验证 使用 1.1.1复制结构 1.1.2 在main.js 按需引入 引入首字母大写&#xff0c;-用大写替代&#xff0c;el省略&#xff0c;一定要引用完整 注意Vue.compon…

Smartbi观点 | ChatGPT还处于初级阶段?然而AI早已打入BI内部

最近&#xff0c;当我们还沉浸在电影《流浪地球2》MOSS所带来的震感时&#xff0c;ChatGPT又火爆社交媒体&#xff0c;成为全球“新顶流”。 官方数据显示&#xff0c;今年1月&#xff0c;平均每天约有1300万独立访客使用 ChatGPT&#xff0c;累计用户超1亿&#xff0c;创下了互…

Java 基础面试题——关键字

目录1.Java 中的关键字是指什么&#xff1f;有哪些关键字&#xff1f;2.instanceof 关键字的作用是什么&#xff1f;3.访问修饰符 public、private、protected、以及不写&#xff08;default&#xff09;时的区别&#xff1f;4.Java 中有没有 goto 关键字?5.在 Java 中&#x…

第一章SpringBoot简介

文章目录什么是SpringBoot了解我们的Spring能干什么Spring的生态为什么需要SpringBootSpringBoot优点SpringBoot缺点SpringBoot的大时代背景微服务分布式分布式的困难分布式的解决云原生上云的困难SpringBoot之Helloworld新添我们的maven相关的配置创建一个maven项目并导入依赖…

Seata-Server分布式事务原理加源码(二) - 分布式事务解决方案

分布式事务解决方案 2PC即两阶段提交协议&#xff0c;是将整个事务流程分为两个阶段&#xff0c;P是指准备阶段&#xff0c;C是指提交阶段。 准备阶段&#xff08;Prepare phase&#xff09;提交阶段&#xff08;commit phase&#xff09; 举例&#xff1a;比如说相亲对象两…

微服务项目【秒杀商品展示及商品秒杀】

登录方式调整 第1步&#xff1a;从zmall-common的pom.xml中移除spring-session-data-redis依赖 注意&#xff1a; 1&#xff09;本次不采用spring-session方式&#xff0c;改用redis直接存储用户登录信息&#xff0c;主要是为了方便之后的jmeter压测&#xff1b; 2&#xff09…

魔兽世界WOW私服架设详细教程

1. 写在前面&#xff1a;此教程是针对国服WOW3.3.5.13930版本的&#xff0c;因为目前魔兽单机在此版本下运行最正常。WOW4.0以上版本还有些许问题2. 准备文件&#xff08;1&#xff09;WOW3.3.5.13930客户端&#xff0c;没有的可以从这里下载WOW 3.3.2安装文件和WOW3.3.2-3.3.5…

【linux C】daemon函数应用之——进程守护小工具,运维仔看了都说好!并附带shell版本

最近接触到Linux C中的daemon函数&#xff0c;顾名思义&#xff0c;它和守护进程Daemon有关&#xff1b;简单来说Linux Daemon&#xff08;守护进程&#xff09;是运行在后台的一种特殊进程&#xff1b; 一般来说&#xff0c;它独立于控制终端并且周期性地执行某种任务或等待处…

认识V模型、W模型、H模型

软件测试与软件工程息息相关&#xff0c;软件测试是软件工程组成中不可或缺的一部分。 在软件工程、项目管理、质量管理得到规范化应用的企业&#xff0c;软件测试也会进行得比较顺利&#xff0c;软件测试发挥的价值也会更大。 要关注软件工程、质量管理以及配置管理与软件测试…

ChatGPT中文网尝鲜,感觉自己快下岗了

最近很火的ChatGPT之初体验 ChatGPT中文网 居然可以回答代码问题 尝试了一下, 它居然说自己是一个人 顺便问了下简单的java代码问题 “使用java语言写一个递归打印D盘中所有文件名的程序” 很流畅的回答了出来,注释还写得比我详细,感觉我离下岗不远了 这就是GPT写的代码 i…

工人不戴安全帽自动检测识别 opencv

工人不戴安全帽自动检测识别通过pythonopencv深度学习网络模型&#xff0c;工人不戴安全帽自动检测识别算法对现场人员穿戴进行全天候不间断识别检测&#xff0c;发现现场人员违规行为着装自动抓拍存档。Python是一门解释性脚本语言。解释性语言&#xff1a;解释型语言&#xf…