openGauss源码解析(48)

openGauss源码解析:存储引擎源码解析(16)

4.2.7 cstore

列存储格式是OLAP类数据库系统最常用的数据格式,适合复杂查询、范围统计类查询的在线分析型处理系统。本节主要介绍openGauss数据库内核中cstore列存储格式的实现方式。

1. cstore整体框架

cstore列存储格式整体框架如图4-21所示。其主要模块代码分布参见4.2.1节。与行存储格式不同,cstore列存储的主体数据文件以CU为I/O单元,只支持追加写操作,因此cstore只有读共享缓冲区。CU间和CU内的可见性由对应的CUDESE表(astore表)决定,因此其可见性和并发控制原理与行存储astore基本相同。

图4-21 cstore列存储格式整体框架示意图

2. cstore存储单元结构

图4-22 CU结构示意图

如图4-22所述,cstore的存储单元是CU,分别包括以下内容。

(1) CU的CRC值,为CU结构中除CRC成员之外,其他所有字节计算出的32位CRC值。
(2) CU的magic值,为插入CU的事务号。
(3) CU的属性值,为16位标志值,包括CU是否包含NULL行、CU使用的压缩算法等CU粒度属性信息。
(4) 压缩后NULL值位图长度,如果属性值中标识该CU包含NULL行,则本CU在实际数据内容开始处包含NULL值位图,此处储存该位图的字节长度,如果该CU不包含NULL行,则无该成员。
(5) 压缩前数据长度,即CU数据内容在压缩前的字节长度,用于读取CU时进行内存申请和校验。
(6) 压缩后数据长度,即CU数据内容在压缩后的字节长度,用于插入CU时进行内存申请和校验。
(7) 压缩后NULL值位图内容,如果属性值中标识该CU包含NULL行,则该成员即为每行的NULL值位图,否则无该成员。
(8) 压缩后数据内容,即实际写入磁盘的CU主体数据内容。

每个CU最多保存对应字段的MAX_BATCH_ROWS行(默认60000行)数据。相邻CU之间按8kB对齐。

CU模块提供的主要CU操作接口如表4-28所示。

表4-28 CU操作接口

函数名称

接口含义

AppendCuData

向组装的CU中增加一行(仅对应字段)

Compress

压缩(若需)和组装CU

FillCompressBufHeader

填充CU头部

CompressNullBitmapIfNeed

压缩NULL值位图

CompressData

压缩CU数据

CUDataEncrypt

加密CU数据

ToVector

将CU数据解构为向量数组结构

UnCompress

解压(若需)和解析CU

UnCompressHeader

解析CU头部内容

UnCompressNullBitmapIfNeed

解压NULL值位图

UnCompressData

解压CU数据

CUDataDecrypt

解密CU数据

3. cstore多版本机制

cstore支持完整事务语义的DML查询,原理如下。

(1) CU间的可见性:每个CU对应CUDESC表(astore行存储表)中的一行记录(一对一),该CU的可见性完全取决于该行记录的可见性。
(2) 同一个CU内不同行的可见性:每个CU的内部可见性对应CUDESC表中的一行(多对一),该行的bitmap字段为最长MAX_BATCH_ROWS个bit的删除位图(bit 1表示删除,bit 0表示未删除),通过该位图记录的可见性和多版本,来支持CU内不同行的可见性。同时由于DML操作都是行粒度操作的,因此对于行号范围相同的、不同字段的多个CU均对应同一行位图记录。
(3) CU文件读写并发控制:CU文件自身为APPEND-ONLY,只在追加时对文件大小扩展进行加锁互斥,无须其他并发控制机制。
(4) 同一个字段的不同CU,对应严格单调递增的cu_id编号,存储在对应的CUDESC表记录中,该cu_id的获取通过图4-24中的文件扩展锁来进行并发控制。
(5) 对于cstore表的单条插入以及更新操作,提供与每个cstore表对应的delta表(astore行存储表),来接收单条插入或单条更新的元组,以降低CU文件的碎片化。

可见,cstore表的可见性依赖于对应CUDESC表中记录的可见性。一个CUDESC表的结构如表4-29所示,其与CU的对应关系如图4-23所示。

表4-29 CUDESC表的结构

字段名

类型

含义

col_id

integer

字段序号,即该cstore列存储表的第几个字段;特殊的,对于CU位图记录,该字段恒为-10

cu_id

oid

CU序号,即该列的第几个CU

min

text

该CU中该字段的最小值

max

text

该CU中该字段的最大值

row_count

integer

该CU中的行数

cu_mode

integer

CU模式

size

bigint

该CU大小

cu_pointer

text

该CU偏移(8k对齐);特殊的,对于CU位图记录,该字段为删除位图的二进制内容

magic

integer

该CU magic号,与CU头部的magic相同,校验用

extra

text

预留字段

图4-23 CUDESC表和CU对应关系示意图

如图4-24、图4-25所示,下面结合并发插入和并发插入查询2种具体场景,介绍openGauss中cstore多版本的具体实现方法。

图4-24 cstore表并发插入示意图

图4-25 cstore表并发插入和查询示意图

1) 并发插入操作

对于并发的插入操作,会话1和会话2首先分别在各自的局部内存中完成待插入CU的拼接。然后假设会话1先获取到cstore表的扩展锁,那么会话2会阻塞在该锁上。在持锁阶段,会话1申请到该字段下一个cuid 1001,预占了该cu文件0 - 6 K的内容(即cuid 1001的内容大小),将cuid的大小、偏移以及cuid 1001头部部分信息填充到CUDESC记录中,并完成CUDESC记录的插入。接着,会话1放锁,并将cuid 1001的内容写入到CU对应偏移处,记录日志,再将删除位图记录插入CUDESC表中。当会话1释放cstore表的扩展锁之后,会话2就可以获取到该锁,然后,类似会话1的后续操作,完成cuid 1002的插入操作。

2) 并发插入和查询操作

假设在上述会话2的插入事务(事务号101)执行过程中,有并发的查询操作执行。对于查询操作,首先基于col_id和cuid这两个索引键对CUDESC表做索引扫描。由于事务号101在查询的快照中,因此cuid 1002的所有记录对于查询事务不可见,查询事务只能看到cuid 1001(事务号100)的那些记录。然后,查询事务根据CUDESC记录中对应的CU文件偏移和CU大小,将cuid 1001的数据从磁盘文件或缓存中加载到局部内存中,并拼接成向量数组的形式返回。

posted @ 2024-04-29 16:21  openGauss-bot  阅读(22)  评论(0编辑  收藏  举报