暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

openGauss UBtree INSERT 代码走读

Hedgehog. 2024-11-11
274

Ustore 存储引擎,又名 In-place Update 存储引擎(原地更新),是 openGauss 内核新增的一种存储模式。UBtree是专门为 Ustore 存储引擎开发的索引。

UBtree 简介

Ubtree在现实是一个 B-Link Tree,并在其基础上增加了事物信息,叶子页面的元组存放堆表元组的 ctid,属于非聚簇索引,叶子节点的元组按 key + ctid(元组所在页面号和页面内元组指针下标)升序排列,结构如下图所示:
UBTREEStructe.png
结构特点为:

  • 是一个多叉树
  • 每一层的节点是有序的
  • 每一层的节点间存在双指针
  • 数据仅保存在叶子节点中,数据是一个指向实际 tuple 的指针
    其中每一个节点都是一个 page,page 的结构如下:
    ubtreeTreePage.png
    索引页头定义如下:PageHeaderData:
typedef struct {     PageXLogRecPtr pd_lsn;    /* 记录该页面最后一次修改操作的xlog的结束位置的下一位 */     uint16 pd_checksum;       /* 页面CRC校验值 */     uint16 pd_flags;          /* 页面的标记位 */     LocationIndex pd_lower;   /* 页面中空闲空间的起始位置,即下一个可行指针的插入位置 */     LocationIndex pd_upper;   /* 页面中空闲空间的结束位置,即下一个可插入tuple的位置 */     LocationIndex pd_special; /* 页尾特殊区域的起始位置 */     uint16 pd_pagesize_version; /* 页面的大小和版本号 */     ShortTransactionId pd_prune_xid;           /* 页面清理辅助事物号,通常为该页面内存在的最老的删除或更新操作的事物号,用于判断是否触发空闲空间回收 */     ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* 行指针数组 */ } PageHeaderData;

行指针定义如下:ItemIdData

typedef struct ItemIdData {     unsigned lp_off : 15, /* 元组的页内偏移量,即Indextuple在页面内的位置 */         lp_flags : 2,     /* 状态值 */         lp_len : 15;      /* tuple字节长度 */ } ItemIdData;

索引元组头结构如下,IndexTupleData:

typedef struct IndexTupleData {     ItemPointerData t_tid; /* heap tuple 的物理行号 */     /* ---------------      * t_info 标志位内容:      *      * 15th (high) bit: 是否有NULL字段      * 14th bit: 是否有变长字段      * 13th bit: 访存方式      * 12-0 bit: 元组大小      * ---------------      */     unsigned short t_info; /* tuple 的标志位 */ } IndexTupleData; /* 实际的index tuple数据紧跟该结构体 */

ubtree会在Index tuple后面再增加一个xmin、xmax信息:IndexTransInfo:

typedef struct IndexTransInfo {     TransactionId xmin;     TransactionId xmax; } IndexTransInfo;

索引页尾定义如下:UBTPageOpaqueDataInternal:

typedef struct UBTPageOpaqueDataInternal {     BlockNumber btpo_prev; /* 左兄弟的块号,用于反向扫描 */     BlockNumber btpo_next; /* 右兄弟的块号,用于正向扫描 */     union {         uint32 level;                /* tree level --- zero for leaf pages */         ShortTransactionId xact_old; /* next transaction ID, if deleted */     } btpo;     uint16 btpo_flags;      /* 页面类型 */     BTCycleId btpo_cycleid; /* 最近的一次分裂的vacuum cycle ID */     TransactionId last_delete_xid; /* 记录页面上最后一次删除事物的XID */     TransactionId xid_base; /* 页面上的 xid-base */     int16 activeTupleCount; /* 对页面上的活跃元组的计数 */ } UBTPageOpaqueDataInternal;

INSERT 流程

下述的ubtinsert函数中仅保留了基本的执行流程,更多细致的处理读者可以访问社区server仓库的src/gausskernel/storage/access/ubtree/ubtree.cpp和src/gausskernel/storage/access/ubtree/ubtinsert.cpp查看更多实现细节。

Datum ubtinsert(PG_FUNCTION_ARGS) { *** //     itup = index_form_tuple(RelationGetDescr(rel), values, isnull, RelationIsUBTree(rel));     itup->t_tid = *htCtid;     /* reserve space for xmin/xmax */     Size newsize = IndexTupleSize(itup) + sizeof(ShortTransactionId) * 2;     IndexTupleSetSize(itup, newsize);     result = UBTreeDoInsert(rel, itup, checkUnique, heapRel);     pfree(itup);     PG_RETURN_BOOL(result); }

函数的入口为 ubtinsert,构造出一个 IndexTuple 后进入 UBTreeDoInsert 主流程;

bool UBTreeDoInsert(Relation rel, IndexTuple itup, IndexUniqueCheck checkUnique, Relation heapRel) { *** //     itupKey = UBTreeMakeScanKey(rel, itup); // 构造一个 BTScanInsert 类型的scan key     *** //     if (!fastpath) {         /* 通过二分查找找到包含当前key的page。         * 查找方式是先找到根节点,在当前层通过UBTreeBinarySearch找到insert所需的节点,         * 并作递归。当找到的节点是叶子节点时,返回该节点。         * 这里会涉及到一个比较函数UBTreeCompare,它会先根据scankey作比较,         * 若未能判断大小,会根据TID做比较。*/         stack = UBTreeSearch(rel, itupKey, &buf, BT_WRITE);     } *** //     if (checkUnique != UNIQUE_CHECK_EXISTING) {         CheckForSerializableConflictIn(rel, NULL, buf);         /* do the insertion         * 找到当前插入的位置         * 1. 校验插入数据大小是否小于页面的三分之一,         * 这个检验是为了保证每个页面可以插入至少三个数据         * 2. 检验unique,如果要插入的数据比当前的highkey的数据更大,         * 则需要将数据插入到下一个页面         * 3. 通过判断插入数据大小,确认是否需要prune,调用UBtreePagePruneOpt做清理         * 4. 通过UBTreeInsertOnPage将IndexTuple插入找到的位置*/         offset = UBTreeFindInsertLoc(rel, &buf, offset, itupKey, itup, checkingunique, stack, heapRel); /* 将 IndexTuple插入找到的位置 * 1. 要插入的数据大于当前页面可用空间,需要进行分裂 * 1) 查找分裂点,选择分裂方式,均分或者根据插入点分裂 * 2) UBTreeSplit根据分裂点,分裂page,并将当前indextuple插入分裂点,记录xlog * 3) UBTreeInsertParent更新父节点 * 2. 要插入的数据小于当前页面值 * 1) 判断是否为当前层的唯一节点,若是则需要保证fast root指向的层大于等于当前层 * 2) UBTreePageAddTuple将数据插入页面 * 3) 创建meta页,cbuf的标记,释放锁 * */         UBTreeInsertOnPage(rel, itupKey, buf, InvalidBuffer, stack, itup, offset, false);     } else {         /* just release the buffer */         _bt_relbuf(rel, buf);     }     *** //     return is_unique; }
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论