2021SC@SDUSC
目录
B-Tree的插入
讲完了B-Tree树的创建过程,我们来讲一下B-Tree树在postgreSQL的插入操作的相关源码。B-Tree的插入是由bt_insert等函数完成的,下面我们就分析一下其源代码。
bt_insert
该函数相当于插入操作的入口函数
bool
btinsert(Relation rel, Datum *values, bool *isnull,
ItemPointer ht_ctid, Relation heapRel,
IndexUniqueCheck checkUnique,
IndexInfo *indexInfo)
{
bool result;
IndexTuple itup;
/* 得到索引元组 */
itup = index_form_tuple(RelationGetDescr(rel), values, isnull);
itup->t_tid = *ht_ctid;
result = _bt_doinsert(rel, itup, checkUnique, heapRel);//调用 _bt_doinsert函数进行插入
pfree(itup);
return result;//返回插入是否成功
}
_bt_doinsert
首先介绍一下相关的重要数据结构BTInsertStateData,该数据结构设计的时候被认为是_bt_doinsert的函数的私有。
BTInsertStateData
typedef struct BTInsertStateData
{
IndexTuple itup; //正在插入的索引元组
Size itemsz; //itup大小
BTScanInsert itup_key; /* 插入的扫描键值*/
/*插入的包含叶子节点的区域 */
Buffer buf;
/*当前的缓冲区的边界缓存,仅用于插入时刻*/
bool bounds_valid;//边界是否有效
OffsetNumber low;//low-high 边界区域
OffsetNumber stricthigh;
} BTInsertStateData;
接下来介绍_bt_doinsert函数的相关内容,源代码较长,我在这提前介绍一下该函数的流程
1:调用_bt_mkscankey计算元组的扫描键值
2:计算fastpath,即是否可以通过缓冲区来走捷径降低时间复杂度,比如多次插入单调递增的值时,就可利用缓冲区获取上一次插入的位置
3:并行操作的相关检查
4:唯一索引的相关检查
5:利用_bt_findinsertloc函数寻找插入位置
6:利用_bt_insertonpg进行插入
7:相关栈和锁的释放
以下是该函数的源码,相关解析在源码中进行了注释
bool
_bt_doinsert(Relation rel, IndexTuple itup,
IndexUniqueCheck checkUnique, Relation heapRel)
{
bool is_unique = false;//是否为唯一索引
BTInsertStateData insertstate;
BTScanInsert itup_key;
BTStack stack = NULL;
Buffer buf;
bool fastpath;
bool checkingunique = (checkUnique != UNIQUE_CHECK_NO);
/* 插入扫描键 */
itup_key = _bt_mkscankey(rel, itup);
if (checkingunique)//检查是否为唯一索引
{
if (!itup_key->anynullkeys)
{
itup_key->scantid = NULL;
}
else
{
checkingunique = false;
Assert(checkUnique != UNIQUE_CHECK_EXISTING);//调用assert函数
//如果该唯一索引不存在那么就赋值is_unique为true,否则就报错
is_unique = true;
}
}
//填写BTinsertState区域,跟踪当前页面和插入位置
insertstate.itup = itup;//索引元组赋值
/* PageAddItem will MAXALIGN(), but be consistent */
insertstate.itemsz = MAXALIGN(IndexTupleSize(itup)); //该索引元组的大小
insertstate.itup_key = itup_key;
insertstate.bounds_valid = false;//设置边界无效
insertstate.buf = InvalidBuffer;
top:
//设定fastpath快速路径,通过查找缓存块来加速插入的过程,初始值为false
fastpath = false;
if (RelationGetTargetBlock(rel) != InvalidBlockNumber)//如果可以获取缓冲块上的排它锁
//排它锁有效遵循快速路径
{
Page page;
BTPageOpaque lpageop;
buf = ReadBuffer(rel, RelationGetTargetBlock(rel));
if (ConditionalLockBuffer(buf))
{
_bt_checkpage(rel, buf);
//用于检查该页面正确性,含有零页或者破损返回
page = BufferGetPage(buf);
//通过缓冲区得到page
lpageop = (BTPageOpaque) PageGetSpecialPointer(page);
/*
查看该页面是否为最右边的页,以及它的空间能否容纳插入一个新元组,以及插入scan键值大于页面的第一个键值
*/
if (P_ISLEAF(lpageop) && P_RIGHTMOST(lpageop) &&
!P_IGNORE(lpageop) &&
(PageGetFreeSpace(page)