华为openGauss数据库源码解析——堆表操作(1)

所谓堆表,是指元组无序存储,数据按照“先来后到”的方式存储在页面中的空闲位置。作为对比,在索引表中,元组根据索引键键值的排序,在页面内部有序存储,且各个页面之间在逻辑上也是有序存储的。堆表存储数据主体,索引表仅存储索引键键值以及对应的、完整元组的物理位置(即完整元组在堆表中的页面号和页内偏移)。

HOT技术(堆内元组技术),在更新时通过修改指针指向定位新元组,而不需要插入相应的索引元组。

在更新tuple1时,会将tuple1(老元祖)的标记位置为heap_hot_update,同时tuple2(新元组)的标记位置为heap_only_tuple。

Heap_Only_tuple说明是hot-chain中的新元组

假设page中有个元组被多次hot更新,只有最老的元组是Heap_hot_Update

只有最新的元组是heap_only_tuple

其他元组既是Heap_hot_Update又是heap_only_tuple

下面对数据库中有关堆表操作的函数进行分析,介绍函数的作用和执行流程,具体细节可以结合openGauss的源码一起浏览。

堆表访问接口函数

Relation heap_open(Oid relationId, LOCKMODE lockmode, int2 bucketid)
#define  heap_close(r,l)  relation_close(r,l)
TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot, int nkeys, ScanKey key, RangeScanInRedis rangeScanInRedis)
static HeapScanDesc heap_beginscan_internal(Relation relation, Snapshot snapshot, int nkeys, ScanKey key,uint32 flags, RangeScanInRedis rangeScanInRedis)
void heap_endscan(TableScanDesc sscan)
void heap_rescan(TableScanDesc sscan, ScanKey key)
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
static void heapgettup(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
static void heapgettup_pagemode(HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key)
void heap_markpos(TableScanDesc sscan)
void heap_restrpos(TableScanDesc sscan)
void heapgetpage(TableScanDesc sscan, BlockNumber page)
Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
int heap_multi_insert(Relation relation, Relation parent, HeapTuple *tuples, int ntuples, CommandId cid, int options,BulkInsertState bistate,HeapMultiInsertExtraArgs *args)
TM_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid,Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool allow_delete_self)
void simple_heap_delete(Relation relation, ItemPointer tid, int options)
TM_Result heap_update(Relation relation, Relation parentRelation, ItemPointer otid, HeapTuple newtup,CommandId cid, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool allow_update_self)
void simple_heap_update(Relation relation, ItemPointer otid, HeapTuple tup)
//判断当前时刻,元组htup是否有效
bool HeapTupleSatisfiesNow(HeapTuple htup, Snapshot snapshot, Buffer buffer)
TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer* buffer, CommandId cid, LockTupleMode mode, bool nowait, TM_FailureData *tmfd, bool allow_lock_self)
void heap_inplace_update(Relation relation, HeapTuple tuple)

heap_open函数

参数:(Oid relationId, LOCKMODE lockmode, int2 bucketid)

数据表Oid,锁类型,桶id(可能是一个内存空间类似于buffer,从这个桶中进行查找数据表)

作用:通过数据表的Oid打开一个表,同时检查是否是索引或者混合类型,如果是报错,否则获取表的相关信息

1.调用relation_open函数,返回数据表信息,然后判断是否是索引或者混合类型,如果是,报错。

2.返回数据表信息。

heap_beginscan函数

作用:初始化扫描表描述信息,并且返回。

1.根据传入参数rangeScanInRedis.isRangeScanInRedis,判断是否需要报告位置给syncscan和是不是page-at-a-time模式,设置flags

2.调用heap_beginscan_internal函数

heap_beginscan_internal函数

作用:返回一个初始化完成的HeapScanDesc

参数:

(Relation relation, Snapshot snapshot, int nkeys, ScanKey key, uint32 flags, RangeScanInRedis rangeScanInRedis)

1.定义一个HeapScanDesc变量

2.分配空间,设置变量的一些参数。

3.调用initscan(scan, key, false);

initscan函数主要也是根据不同情况设置HeapScanDesc的参数,还拷贝了scan->rs_base.rs_key,也就是扫描关键字的描述数组。

4.返回这个变量。

heap_endscan函数

作用:结束关系扫描,释放扫描的缓存

1.获取表扫描描述符

2.释放缓存、关系引用次数减一、释放rs_base.rs_key(扫描关键字的描述数组)、释放访问策略

3.释放表扫描描述符。

heap_rescan函数

作用:重新初始化扫描描述符

调用initscan函数

heap_getnext函数

作用:返回扫描的下一条元组,通过scan->rs_ctup返回

1.根据是否是page-at-a-time模式,调用heapgettup_pagemode或者heapgettup函数。(扫描的重点,复杂)

2.返回scan->rs_ctup

heapgettup函数

作用:获取下一条元组

参数:HeapScanDesc scan, ScanDirection dir, int nkeys, ScanKey key

1.首先根据ScanDirection进行分类Forword、Backward、no movement

Forword:

(2).如果scan没有初始化,就先对其进行初始化,设置偏移量为第一条元组的偏移量

已经初始化的scan,设置偏移量line_off为下一条元组的偏移量

(3).获取当前page剩余的未扫描的偏移量lines_left

Backward:

(2).获取当前扫描的页码

(3).如果scan没有初始化,偏移量就是当前页最大偏移量,或则获取上一个偏移量元组。当前page剩余未扫描的偏移量lines_left就是当前设置的偏移量。

No movement(重新获取记录):

(2).重新获取当前扫描的元组,获取当前的偏移量

(3).返回

2.根据页号和偏移量获取行指针linepoint

3.先判断lines_left是否大于0,即当前page剩余未扫描的偏移量是否大于0,然后再判断获取的行指针linepoint是否有效,如果有效就获取linepoint对应的元组,设置参数,然后返回

4.如果找到的linepoint无效,那就再继续沿着扫描方向,查找下一个元组,直到找到有效的元组进入第三步,或者lines_left = 0。

5.lines_left = 0说明这一页已经全部扫描过了,那就寻找next_page,然后根据扫描方向,从头或者从尾开始扫描,直到找到有效的linepoint,或者页已经扫描完了返回null。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

allwellright

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值