openGauss源码解析(72)

openGauss源码解析:事务机制源码解析(4)

5.2.2 事务ID分配及CLOG/CSNLOG

为了在数据库内部区别不同的事务,openGauss数据库会为它们分配唯一的标识符,即事务id(transaction id,缩写xid),xid是uint64单调递增的序列。当事务结束后,使用CLOG记录是否提交,使用CSNLOG(commit sequence number log)记录该事务提交的序列,用于可见性判断。

1. 64位xid及其分配

openGauss对每一个写事务均会分配一个唯一标识。当事务插入时,会将事务信息写到元组头部的xmin,代表插入该元组的xid;当事务进行更新和删除时,会将当前事务信息写到元组头部的xmax,代表删除该元组的xid。当前事务id的分配采用的是uint64单调递增序列,为了节省空间以及兼容老的版本,当前设计是将元组头部的xmin/xmax分成两部分存储,元组头部的xmin/xmax均为uint32的数字;页面的头部存储64位的xid_base,为当前页面的xid_base。

元组结构如图5-8所示,页面头结构如图5-9所示,那么对于每一条元组真正的xmin、xmax计算公式即为:元组头中xmin/xmax + 页面xid_base。

图5-8 元组结构

HeapPageHeader

图5-9 页面头结构

当页面不断有更大的xid插入进来时,可能超过“xid_base + 232”,此时需要通过调节xid_base来满足所有元组的xmin/xmax都可以通过该值及元组头部的值计算出来,详细逻辑见“2. CLOG、CSNLOG”内“3) 关键函数:”中的第(3)小节。

为了使xid不消耗过快,openGauss当前只对写事务进行xid的分配,只读事务不会额外分配xid,也就是说并不是任何事务一开始都会分配xid,只有真正使用xid时才会去分配。在分配子事务xid时,如果父事务还未分配xid,则会先给父事务分配xid,再给子事务分配xid,确保子事务的xid比父事务大。理论上64位xid已经足够使用:假设数据库的tps为1000万,即1秒钟处理1000万个事务,64xid可以使用58万年。

2. CLOG、CSNLOG

CLOG以及CSNLOG分别维护事务ID->CommitLog以及事务ID->CommitSeqNoLog的映射关系。由于内存的资源有限,并且系统中可能会有长事务存在,内存中可能无法存放所有的映射关系,此时需要将这些映射写盘成物理文件,所以产生了CLOG(XID->CommitLog Map)、CSNLOG(XID->CommitSeqNoLog Map)文件。CSNLOG以及CLOG均采用了SLRU(simple least recently used,简单最近最少使用)机制来实现文件的读取及刷盘操作。

1) CLOG用于记录事务id的提交状态。openGauss中对于每个事务id使用4个bit位来标识它的状态。CLOG定义代码如下:

#define CLOG_XID_STATUS_IN_PROGRESS 0x00 表示事务未开始或还在运行中(故障场景可能是crash)

#define CLOG_XID_STATUS_COMMITTED 0x01 表示该事务已经提交

#define CLOG_XID_STATUS_ABORTED 0x02 表示该事务已经回滚

#define CLOG_XID_STATUS_SUB_COMMITTED 0x03 表示子事务已经提交而父事务状态未知

CLOG页面的物理组织形式如图5-10所示。

图5-10 CLOG页面的物理组织形式

图5-10标识事务1、4、5还在运行中,事务2已经提交,事务3已经回滚。

2) CSNLOG用于记录事务提交的序列号。openGauss为每个事务id分配8个字节uint64的CSN号,所以一个8kB页面能保存1k个事务的CSN号。CSNLOG达到一定大小后会分块,每个CSNLOG文件块的大小为256kB。同xid号类似,CSN号预留了几个特殊的号。CSNLOG定义代码如下:

#define COMMITSEQNO_INPROGRESS UINT64CONST(0x0) 表示该事务还未提交或回滚

#define COMMITSEQNO_ABORTED UINT64CONST(0x1) 表示该事务已经回滚

#define COMMITSEQNO_FROZEN UINT64CONST(0x2) 表示该事务已提交,且对任何快照可见

#define COMMITSEQNO_FIRST_NORMAL UINT64CONST(0x3) 事务正常的CSN号起始值

#define COMMITSEQNO_COMMIT_INPROGRESS (UINT64CONST(1) << 62) 事务正在提交中

同CLOG相似,CSNLOG的物理结构体如图5-11所示。

5

7

10

6

8

4

第*页

8个字节长度

表示存储对应事务的CSN空间

CSN log物理存储文件内容:

2048

2050

2051

2052

2053

2049

文件所对应的事务id逻辑编号:

第*页

图5-11 CSNLOG的物理结构体

事务id 2048、2049、2050、2051、2052、2053的对应的CSN号依次是5、4、7、10、6、8;也就是说事务提交的次序依次是2049->2048->2052->2050->2053->2051。

3) 关键函数

64位xid页面xid_base的计算函数:

(1) Heap_page_prepare_for_xid函数:在对页面有写入操作时调用,用来调节xid_base。

➀ 新来xid在“xid_base + FirstNormalxid”与“xid_base + MaxShortxid(0xFFFFFFFF)”之间时,当前的xid_base不需要调整。

➁ 新来xid在“xid_base + FirstNormalxid”左侧(xid小于该值)时,需要减小xid_base。

➂ 新来xid在“xid_base + MaxShortxid”右侧(xid大于该值)时,需要增加xid_base。

➃ 特殊情况下,由于页面的xid跨度大于32位能表示的范围时,就需要冻结掉本页面上较小的xid,即将提交的xid设为FrozenTransactionId(2),该值对所有事务均可见;将回滚的xid设为InvalidTransactionId(0),该值对所有的事务均不可见。

(2) Freeze_single_heap_page函数:对该页面上较小的xid进行冻结操作。

➀ 计算oldestxid,比该值小的事务已经无任何事务访问更老的版本,此时可以将提交的xid直接标记为FrozenTransactionId,即对所有事务可见;将回滚的xid标记为InvalidTransactionId,即对所有事务不可见。

➁ 页面整理,清理hot update链,重定向itemid,整理页面空间。

➂ 根据oldestxid处理各个元组。

(3) Heap_page_shift_base函数:更新xid_base,调整页面中各个元组头中的xmin/xmax。
(4) GetNewTransactionId函数:获取最新的事务id。
posted @ 2024-04-30 09:53  openGauss-bot  阅读(31)  评论(0编辑  收藏  举报