postgreSQL源码分析——索引的建立与使用——B-Tree索引(2)
2021SC@SDUSC
目录
- B-Tree建立过程
- IndexAmRoutine
- BTBuildState
- BTWriteState
- btbuild()
- _bt_leafbuild
- _bt_load
- bt_buildadd
- 总结
B-Tree建立过程
上次我们讲了关于nbtree的相关算法原理和相关的数据结构,这次我们具体分析nbtree的关于B-Tree索引创建流程的源码。
IndexAmRoutine
我们在第二篇文章讲过索引的使用是由下层接口函数和上层统一操作函数来进行的,在nbtree.c中bthanlder()函数就是返回一个IndexAmRoutine,统一管理下层接口函数,为上层操作函数统一调度。
//Datum
bthandler(PG_FUNCTION_ARGS)
{IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);//返回IndexAmRoutine的对象amroutine//下面是对amroutine相关参数和处理函数进行赋值amroutine->amstrategies = BTMaxStrategyNumber;amroutine->amsupport = BTNProcs;amroutine->amcanorder = true;amroutine->amcanorderbyop = false;amroutine->amcanbackward = true;amroutine->amcanunique = true;amroutine->amcanmulticol = true;amroutine->amoptionalkey = true;amroutine->amsearcharray = true;amroutine->amsearchnulls = true;amroutine->amstorage = false;amroutine->amclusterable = true;amroutine->ampredlocks = true;amroutine->amcanparallel = true;amroutine->amcaninclude = true;amroutine->amkeytype = InvalidOid;
//关于bttree索引建立,插入,删除,扫描等处理函数进行赋予具体的操作函数,
//这些函数均在之后的代码中有声明amroutine->ambuild = btbuild;amroutine->ambuildempty = btbuildempty;amroutine->aminsert = btinsert;amroutine->ambulkdelete = btbulkdelete;amroutine->amvacuumcleanup = btvacuumcleanup;amroutine->amcanreturn = btcanreturn;amroutine->amcostestimate = btcostestimate;amroutine->amoptions = btoptions;amroutine->amproperty = btproperty;amroutine->ambuildphasename = btbuildphasename;amroutine->amvalidate = btvalidate;amroutine->ambeginscan = btbeginscan;amroutine->amrescan = btrescan;amroutine->amgettuple = btgettuple;amroutine->amgetbitmap = btgetbitmap;amroutine->amendscan = btendscan;amroutine->ammarkpos = btmarkpos;amroutine->amrestrpos = btrestrpos;amroutine->amestimateparallelscan = btestimateparallelscan;amroutine->aminitparallelscan = btinitparallelscan;amroutine->amparallelrescan = btparallelrescan;PG_RETURN_POINTER(amroutine);
}
BTBuildState
当建立索引时为了保存索引元组建立了BTBuildState的数据结构,其代码如下
typedef struct BTBuildState
{bool isunique;//是否为唯一索引bool havedead;//是否有标记删除的元组Relation heap;//相关的堆BTSpool *spool;//保存生成的元组BTSpool *spool2;//保存"dead tuple"对应的元组double indtuples;//保存的元组的总和BTLeader *btleader;
} BTBuildState;
BTWriteState
typedef struct BTWriteState
{Relation heap;Relation index;BTScanInsert inskey; /* generic insertion scankey */bool btws_use_wal; /* 是否开启wal日志 */BlockNumber btws_pages_alloced; /* #申请的页面的块号 */BlockNumber btws_pages_written; /* #写入的页面的块号*/Page btws_zeropage; /* 需要填充0值得页面*/
} BTWriteState;
btbuild()
建立索引的入口函数为btbuild()函数,它的代码如下
IndexBuildResult *
btbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{IndexBuildResult *result;BTBuildState buildstate;double reltuples;#ifdef BTREE_BUILD_STATSif (log_btree_build_stats)ResetUsage();
#endif
//设置BTBuildState的初值buildstate.isunique = indexInfo->ii_Unique;buildstate.havedead = false;buildstate.heap = heap;buildstate.spool = NULL;buildstate.spool2 = NULL;buildstate.indtuples = 0;buildstate.btleader = NULL;/*希望精确调用索引关系一次*/if (RelationGetNumberOfBlocks(index) != 0)elog(ERROR, "index \"%s\" already contains data",RelationGetRelationName(index));reltuples = _bt_spools_heapscan(heap, index, &buildstate, indexInfo);
//表元组封装成索引元组_bt_leafbuild(buildstate.spool, buildstate.spool2);//spool文件的排序_bt_spooldestroy(buildstate.spool);//清除spool和它的子结构if (buildstate.spool2)_bt_spooldestroy(buildstate.spool2);//清除spool2if (buildstate.btleader)_bt_end_parallel(buildstate.btleader);//结束并行扫描result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));result->heap_tuples = reltuples;result->index_tuples = buildstate.indtuples;#ifdef BTREE_BUILD_STATSif (log_btree_build_stats){ShowUsage("BTREE BUILD STATS");ResetUsage();}
#endif /* BTREE_BUILD_STATS */return result;
}
_bt_leafbuild
btbuild会调用关于索引建立时生成的索引结构函数为_bt_leafbuild,会对索引元组进行排序
static void
_bt_leafbuild(BTSpool *btspool, BTSpool *btspool2)
{BTWriteState wstate;//建立BTWriteState的数据结构#ifdef BTREE_BUILD_STATSif (log_btree_build_stats){ShowUsage("BTREE BUILD (Spool) STATISTICS");ResetUsage();}
#endif /* BTREE_BUILD_STATS */pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE,PROGRESS_BTREE_PHASE_PERFORMSORT_1);tuplesort_performsort(btspool->sortstate);//进行排序if (btspool2){pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE,PROGRESS_BTREE_PHASE_PERFORMSORT_2);tuplesort_performsort(btspool2->sortstate);//如果存在唯一索引也会进行排序}wstate.heap = btspool->heap;//对于wstate赋值wstate.index = btspool->index;wstate.inskey = _bt_mkscankey(wstate.index, NULL);/*在WAL进行操作*/wstate.btws_use_wal = XLogIsNeeded() && RelationNeedsWAL(wstate.index);/* reserve the metapage */wstate.btws_pages_alloced = BTREE_METAPAGE + 1;//元页加一wstate.btws_pages_written = 0;//写入的页号为0wstate.btws_zeropage = NULL; /* until needed */pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE,PROGRESS_BTREE_PHASE_LEAF_LOAD);_bt_load(&wstate, btspool, btspool2);
}
_bt_load
_bt_load函数是在 _bt_leafbuild中被调用,将排好序的spool依次取出。源代码如下:
static void
_bt_load(BTWriteState *wstate, BTSpool *btspool, BTSpool *btspool2)
{BTPageState *state = NULL;bool merge = (btspool2 != NULL);IndexTuple itup,itup2 = NULL;bool load1;TupleDesc tupdes = RelationGetDescr(wstate->index);int i,keysz = IndexRelationGetNumberOfKeyAttributes(wstate->index);SortSupport sortKeys;int64 tuples_done = 0;if (merge)//如果spool2元组为非空{/*需要将spool和spool2进行归并,按照偏序关系依次取出索引元组*//* 归并前准备 */itup = tuplesort_getindextuple(btspool->sortstate, true);itup2 = tuplesort_getindextuple(btspool2->sortstate, true);/* 为每一列准备SortSupport data */sortKeys = (SortSupport) palloc0(keysz * sizeof(SortSupportData));for (i = 0; i < keysz; i++){SortSupport sortKey = sortKeys + i;ScanKey scanKey = wstate->inskey->scankeys + i;int16 strategy;sortKey->ssup_cxt = CurrentMemoryContext;sortKey->ssup_collation = scanKey->sk_collation;sortKey->ssup_nulls_first =(scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0;sortKey->ssup_attno = scanKey->sk_attno;/*不支持缩写 */sortKey->abbreviate = false;AssertState(sortKey->ssup_attno != 0);strategy = (scanKey->sk_flags & SK_BT_DESC) != 0 ?BTGreaterStrategyNumber : BTLessStrategyNumber;PrepareSortSupportFromIndexRel(wstate->index, strategy, sortKey);}//按照偏序关系决定次序,load1为true时为spool中元组,为false为spool2元组//判断顺序后调用bt_buildadd函数for (;;){load1 = true; /* load BTSpool next ? */if (itup2 == NULL){if (itup == NULL)break;}else if (itup != NULL){int32 compare = 0;for (i = 1; i <= keysz; i++){SortSupport entry;Datum attrDatum1,attrDatum2;bool isNull1,isNull2;entry = sortKeys + i - 1;attrDatum1 = index_getattr(itup, i, tupdes, &isNull1);attrDatum2 = index_getattr(itup2, i, tupdes, &isNull2);compare = ApplySortComparator(attrDatum1, isNull1,attrDatum2, isNull2,entry);if (compare > 0){load1 = false;break;}else if (compare < 0)break;}/*如果compare值相等,需要比较ItemPointer*/if (compare == 0){compare = ItemPointerCompare(&itup->t_tid, &itup2->t_tid);Assert(compare != 0);if (compare > 0)load1 = false;}}elseload1 = false;/* When we see first tuple, create first index page */if (state == NULL)state = _bt_pagestate(wstate, 0);if (load1){_bt_buildadd(wstate, state, itup);itup = tuplesort_getindextuple(btspool->sortstate, true);}else{_bt_buildadd(wstate, state, itup2);itup2 = tuplesort_getindextuple(btspool2->sortstate, true);}/* Report progress */pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE,++tuples_done);}pfree(sortKeys);}else //如果spool2不存在,直接依次读取spool元组调用bt_buildadd函数{while ((itup = tuplesort_getindextuple(btspool->sortstate,true)) != NULL){if (state == NULL)state = _bt_pagestate(wstate, 0);_bt_buildadd(wstate, state, itup);pgstat_progress_update_param(PROGRESS_CREATEIDX_TUPLES_DONE,++tuples_done);}}
bt_buildadd
_bt_load函数会调用bt_buildadd函数,由于bt_buildadd函数代码很长,在这里大概讲一下该函数的原理,bt_buildadd函数会在页面上直接进行插入,而如果页面不够时会申请新的页面作为它的右兄弟节点,并设置其highkey。并同时查找相应的父节点,如果没有父节点则会新建一个,并赋予最小值,实现了递归调用。该函数的流程图如下
总结
以上便是B-Tree索引的构建的基本流程,由bt_build函数进入,调用bt_leafbuild函数对于spool元组排序,它在调用bt_load函数将排好序的spool元组取出,最后由bt_buildadd函数进行索引结构的实现。可见pg中的B-Tree索引实现层层递进,结构清晰,安全性很高。
postgreSQL源码分析——索引的建立与使用——B-Tree索引(2)相关推荐
- PostgreSQL源码分析
PostgreSQL源码结构 PostgreSQL的使用形态 PostgreSQL采用C/S(客户机/服务器)模式结构.应用层通过INET或者Unix Socket利用既定的协议与数据库服务器进行通信 ...
- postgreSQL源码分析——索引的建立与使用——GIST索引(2)
2021SC@SDUSC 本篇博客主要讲解GiST索引创建以及删除的相关函数 这里写目录标题 GIST创建 相关数据结构 GISTBuildState GISTInsertStack gistbuil ...
- postgreSQL源码分析——索引的建立与使用——Hash索引(3)
2021SC@SDUSC 上一篇博客讲了关于Hash索引创建与插入的相关函数,这一篇博客讲述关于溢出页的操作函数以及Hash表的扩展相关的函数. 目录 溢出页的分配和回收 _hash_addovflp ...
- postgreSQL源码分析——索引的建立与使用——Hash索引(2)
2021SC@SDUSC 目录 Hash索引创建 hashbuild函数 _hash_init函数 Hash索引的插入 hashinsert函数 _hash_doinsert函数 总结 Hash索引创 ...
- postgreSQL源码分析——索引的建立与使用——各种索引类型的管理和操作(2)
2021SC@SDUSC 目录 上层操作函数 index_open index_beginscan() index_create() indexcmd.c 下层接口函数 IndexScanDescDa ...
- postgreSQL源码分析——索引的建立与使用——Hash索引(1)
2021SC@SDUSC 目录 Hash索引 Hash索引原理 Hash表 Hash索引结构 Hash的页面结构 元页 桶页,溢出页,位图页 和B-Tree相比的优缺点 优点 缺点 总结 Hash索引 ...
- postgreSQL源码分析——索引的建立与使用——各种索引类型的管理和操作(1)
2021SC@SDUSC 目录 概述 管理索引的系统表 记录索引相关的系统表 与索引系统表相关的后端源码 索引的操作函数 上层操作函数 下层接口函数 概述 索引是指按表中某些关键属性或表达式建立元组的 ...
- postgreSQL源码分析——索引的建立与使用——总结篇
2021SC@SDUSC 在小组中我负责索引的建立与使用的相关部分,在此一共写了16篇相关的分析报告,着重分析各种索引的操作和管理方法,以及分析了PG中四种最重要的索引B-Tree索引,Hash索引, ...
- postgreSQL源码分析——索引的建立与使用——B-Tree索引(3)
2021SC@SDUSC 目录 B-Tree的插入 bt_insert _bt_doinsert BTInsertStateData _bt_search函数 _bt_moveright函数 B-Tr ...
最新文章
- 继承QTreeWidgetItem发生error: 'staticMetaObject' is not a member of 'QTreeWidgetItem' 错误
- Android系统服务
- java中常用的一些方法
- 【几个关于CSS的网站】
- LinkedList的线程安全解决办法
- c语言遗传算法在求解tsp问题,C语言遗传算法在求解TSP问题设计.doc
- Android小游戏------猜数字
- 史上最全最新手机号码号段大全
- 目不暇接!华为2020春季新品发布会群英荟萃
- 时间序列分析的模型应用 – 股价预测
- 青海干部网络学院 自动学习网站
- USYD悉尼大学DATA1002 详细作业解析Module4
- 显示杂谈(1)-Gamma到底是个什么鬼
- 家谱只能记家族好事吗?家丑不可外扬,后人读谱一脸懵?
- linux操作系统. 80188,Materials-Studio5.5在Linux服务器上安装与测算讨论 - 第一原理 - 小木虫 - 学术 科研 互动社区...
- 数据结构与算法笔记:哈希表——力扣389
- 网狐大联盟AI不进入桌子问题解决
- 手机忘记在出租车上寻回的一次经历
- 数据结构— —单链表
- 网龙网络控股有限公司公布二零一八年中期业绩
热门文章
- Linux网络转发和端口映射的笔记
- 【Flink】Flink 报错 Hash join exceeded Too many duplicate keys
- 【guava】guava 11.0.2 版本 key 肯能丢数据的bug
- 【hortonworks/registry】创建嵌套结构的scheam和带空值的schema
- mac vim 无颜色 增加颜色
- 【impala】impala的shell命令使用
- 01-eclipse打包运行程序总是报错java.lang.NoClassDefFoundError和ava.lang.ClassNotFoundException(打包原理)
- 云计算实战系列十四(MySQL基础)
- XP系统计算机桌面图标不见,XP系统显示桌面图标消失的解决方法
- 有啥不同?来看看Spring Boot 基于 JUnit 5 实现单元测试