上一篇内容我们介绍了openGauss存储技术——行存储引擎,本文重点介绍openGauss列存储引擎。

openGauss列存储引擎

传统行存储数据压缩率低,必须按行读取,即使读取一列也必须读取整行。在分析性的作业以及业务负载的情况下,数据库往往会遇到针对大量表的复杂查询,而这种复杂查询中往往仅涉及一个较宽(表列数较多)的表中个别列。此类场景下,行存储以行作为操作单位,会引入与业务目标数据无关的数据列的读取与缓存,造成了大量IO 的浪费,性能较差。因此openGauss提供了列存储引擎的相关功能。创建表的时候,可以指定行存储还是列存储。

总体来说,列存储有以下优势:

(1)列的数据特征比较相似,适合压缩,压缩比很高,在数据量较大(如数据仓库) 场景下会节省大量磁盘空间,同时也会提高单位作业下的IO 效率。(2)当表中列数比较多,但是访问的列数比较少时,列存储可以按需读取列数据,大大减少不必要的读IO,提高查询性能。(3)基于列批量数据向量运算,结合向量化执行引擎,CPU 的缓存命中率比较高,性能比较好,更适合 OLAP大数据统计分析的场景。

(4)列存储表同样支持 DML操作和 MVCC,功能完备,且在使用角度上做了良好的兼容,基本是对用户透明的,方便使用。

(一)列存储引擎的总体架构

列存储引擎的存储基本单位是 CU(Compression Unit,压缩单元),即表中一列的一部分数据组成的压缩数据块。行存储引擎中是以行作为单位来管理,而当使用列存储时,整个表整体按照不同列划分为若干个 CU,划分方式如图1所示。

图1 CU 划分方式

如图1所示,假设以6万行作为一个单位,则一个12万行、4列宽的表被划分为8个 CU,每个 CU 对应一个列上的6万个列数据。图中有列0、列1、列2、列3四列,数据按照行切分了两个行组(Row Group),每个行组有固定的行数。针对每个行组按照列做数据压缩,形成 CU。每个行组内部各个列的 CU 的行边界是完全对齐的。当然,大部分时候,CU 在经过压缩后,因为数据特征与压缩率的不同,文件大小会完全不同,如图2所示。

图2 示意图

为了管理表对应的CU,与执行器层进行对接来提供各种功能,列存储引擎使用了CUDesc(压缩单元描述符)表来记录一个列存储表中CU 对应的元信息,如图3所示。

图3 列存储引擎整体架构图

注:Cmn表示第 m 列的、CUid是n(第n个)的压缩单元。每个 CU 对应一个 CUDesc的记录,在 CUDesc里记录了整个 CU 的事务时间戳信息、CU 的大小、存储位置、magic校验码、min/max等信息。

与此同时,每张列存储表还配有一张 Delta表,Delta表自身为行存储表。当有少量的数据插入到一张列存储表时,数据会被暂时放入 Delta表,等到到达阈值或满足一定条件或操作时再行整合为 CU 文件。Delta表可以帮助避免单点数据操作带来的加重的 CU 操作与开销。

设计采用级别的多版本并发控制,删除通过引入虚拟列映射 (Virtual Column Bitmap)来标记删除。映射(Bitmap)是多版本的。

(二)列存储的页面组织结构

上文讲到了CUDesc表及其用来记录元信息的目的。CUDesc的典型结构如图4所示。

图4  CUDesc的典型结构

其中:

(1)_rowTupleHeader为传统行存储记录的行头,其中包含了前面提到过的事务及位置信息等,用来进行可见性判断等。(2)cu_mode实际为此 CUDesc对应 CU 的infomask,记录了一些 CU 的特征信息(比如是否为 Full,是否有 NULL等)。(3)magic是 CUDesc与 CU 文件之间校验的关键信息。(4)min/max(最小值/最大值)为稀疏索引,后续会进一步展开介绍。

而 CU 文件结构如图5所示。

图5 文件结构

列存储在 CUDesc表的存储信息基础上设计了一套与上层交互的操作 API。除了上面列存储的页面组织结构以及文件管理中天然可以展示出的结构机制之外,列存储还有如下一些关键的技术特征:

(1)列存储的 CU 中数据的删除,实际上是标记的删除。删除操作,相当于更新了CUDesc表中CU 对应CUDesc记录的删除位图(delete bitmap)结构,标记列中某行对应数据已被删除,而CU 文件数据不会被更改。这样可以避免删除操作带来大量的IO开销及压缩、解压的高额 CPU 开销。这样的设计,也可以使得对于同一个 CU 的查询(select)和删除(delete)互不阻塞,提升并发能力。(2)列存储CU 中数据更新,则是遵循仅允许追加(append-only)原则的,即CU 文件仅会向后进行延展扩充,抑或是启用新的 CU 文件,而不是就对应行在 CU 中的位置就地更新。(3)由于 CU 以及 CUDesc的元数据管理模式,原有系统中的 Vacuum 机制实际上并不会非常有效地清除 CU 中已经失效的存储空间,因为 LazyVacuum(清理数据时,只是标识无用行的状态,使得空间可以复用,不会影响对表数据的操作)仅能在CUDesc级别进行操作,在多数场景下无法对 CU文件本身进行清理。列存储内部如果要对列存储数据表进行清理,需要执行 VacuumFull(除了清理无用行,还会合并数据块,整个过程会锁定表)操作。

(三)列存储的 MVCC设计

理解了 CU、CUDesc的基本结构,以及 CUDesc的管理,或者说是其“代理”角色,列存储的 MVCC设计以及管理,实际上就非常好理解了。

由于列存储的操作基本单位 CU 是由 CUDesc表中的行进行管理的,因此列存储表的CU 可见性判断也是由CUDesc的行头信息,按照传统的行存储可见性进行判断的。

同样的,列存储可见性的单位也是CU 级别(CUDesc),不同于行存储的 Tuple级别。

列存储表的并发控制是 CU 文件级别的,实际上也等同于其 CUDesc代理表的CUDesc行之间的并发控制。多个事务之间在一个 CU 上的并发管控,实际上取决于其在对应的 CUDesc记录上是否冲突。例如:

(1)两个事务并发去读一个CU 是可行的,两个事务都可以拿到此CU 对应 CUDesc 行级别的共享锁(sharelock)。(2)两个事务并发去更新一个 CU,会因为在 CUDesc上的锁冲突而触发一个事务回滚[当然,如果是读已提交(read committed)隔离级别并打开允许并发更新的开关,这里会做的事情是拿到此 CUDesc最新版 本 的 ctid,然后重运行一部分查询树 (queryTree)来进行更新操作。此部分内容,后面文章将会介绍]。(3)两个事务并行执行,一个事务对一个 CU 执行了删除操作并先行提交,则另一个事务在可重读(repeatableread)的隔离级别下,其获取的快照只能看到这个CUDesc在操作发生前的版本,这个版本的 CUDesc中的删除位图(delete_bitmap)对应数据没有被标记删除,也由于 CU 的行删除是标记删除的机制,因此数据在原有 CU 的数据文件中依旧可用,此事务依旧可以在其对应的快照下读到对应行。

删除 CU 中部分数据所进行的实际操作如图6所示。

图6 删除 CU 中部分数据所进行的实际操作

从上面的几个例子可以看出,列存储对于更新的仅允许追加策略以及对于删除操作的标记删除方式,对于列存储事务 ACID的支持,是至关重要的。

(四)列存储的索引设计 

列存储支持的索引设计有:

■ B树索引;

■ 稀疏索引;

■ 聚簇索引。

1.列存储的B树索引 

列存储引擎在 B树索引的支持角度,与传统的行存储引擎无本质差别。对于一般用于应对大数据批量分析性负载的列存储引擎来说,B树索引有助于帮助列存储大大提升自身的点查效率,更好地适应混合负载。

行存储相关 B树索引的索引页面上,存储的是key→ctid(键→行号)的映射,在列存储的场景下,这个映射依旧为key→ctid,但列存储的结构并不能像行存储一样,通过ctid中的块号(block number)和偏移量(offset)直接找到此行数据在数据文件页面中的位置。列存储ctid中记录的是(cu_id,offset),要通过 CUDesc结构来进行查找。

在基于 B树索引的扫描中,从索引中拿到ctid后,需要在对应的 CUDesc表中,根据 CUDesc在cu_id列的索引找到对应的 CUDesc记录,并由此打开对应的 CU 文件,根据偏移量找到数据。

如果此操作设计大量的存储层性能开销,因此列存储的 B树索引,与列存储的其他操作一样,统一都为批量操作,会根据 B树索引找到ctid的集合,然后对此集合进行排序,再批量地对排序后的ctid进行 CU 文件级别的查找与操作。这样可以做到顺序单调地进行索引遍历,大大减少了反复操作文件带来的 CPU 以及IO 开销。

2.列存储的稀疏索引

列存储引擎每个列自带 min/max稀疏索引,每个CUDesc存储该CU 的最小值和最大值。

那么在查询的时候,可以根据查询条件做简单的 min/max判断,如果查询条件不在(min,max)范围内,肯定不需要读取这个 CU,可以大大地减少IO 读取的开销,稀疏索引如图7所示。

图7稀疏索引

注:txn_info表示事务信息;CUPtr表示压缩单元的指针;CU-None表示肯定不命中;CU-Some表示可能有数据匹配;CU_Full表示压缩单元数据全命中。

3.列存储的聚簇索引

列存储表在建立时可以选择在列上建立聚簇索引(partial sort index)。

如果业务的初始数据模型较为离散,那么稀疏索引在不同 CU 之间的 min、max会有大量交集,这种情况下在给定谓词对列存储表进行检索的过程中,会出现大量的CU 误读取,甚至可能导致其查询效率与全表扫描近似。如图8所示,查询2基本命中了所有 CU,min/max索引没有能够有效筛选。

图8数据模型较为离散时的查询效果图

聚簇索引可以对部分区间内的数据做相应的排序(一般区间会包含多个CU所覆盖的行数),可以保证 CU 之前交集尽量少,可以极大地提升在数据离散场景下稀疏索引的效率。

其示意图如图9和图10所示。

图9 聚簇索引生效前

图10 聚簇索引生效后

同时,聚簇索引会使得 CU 内部的数据临近有序,提升 CU 文件本身的压缩比以及压缩效率。

(五)列存储自适应压缩

每个列自适应选择压缩,支持差分编码(delta value encoding)、游 程 编 码 (Run length encoding)、字典编码(dictionary encoding)、LZ4、zlib等混合压缩。根据数据特性的不同,压缩比一般可以有3X~20X。

列存储引擎支持低、中、高三种压缩级别,用户在创建表的时候可以指定压缩级别。

导入1TB原始数据量,分别测试低、中、高三种压缩级别,入库后数据大小分别是100GB、73GB、61GB,如图11所示。

图11压缩比示意图

每次数据导入,首先对每列的数据按照向量组装,对前几批数据做采样压缩,根据数值类型和字符串类型,选择尝试不同的压缩算法。一旦采样压缩完成后,接下来的数据就选择优选的压缩算法了。如图12所示,面向列的自适应压缩主要分为数值压缩和字符压缩。其中对 Numeric小数类型,会转换为整数后,再进行数值压缩。对数值型字符串,也会尝试转换为整数再进行数值压缩。

图12 面向列的自适应压缩

(六)列存储的持久化设计

在列存储的组织结构与 MVCC机制的介绍中提到,列存储的存储单位由 CUDesc和CU文件共同组成,其中 CUDesc记录了CU相关的元信息,控制其可见性,实际上充当了一个 “代 理”的角色。但是CUDesc和CU,实质上还是分离的文件状态。CUDesc表本质上还是行存储表,其持久化流程遵从行存储的共享缓冲区脏页与 Redo日志的持久化流程,在事务提交前,CUDesc的改动会被记录在 Redo日志中进行持久化。单个 CU 文件本身,由于含有大量的数据,使用正常的事务日志进行持久化需要消耗大量的事务日志,引入非常大的性能开销,并且恢复也十分缓慢。因此根据其应用场景,仅允许追加(append-only)的属性及与 CUDesc的对应关系,列存储的 CU 文件,为了确保 CUDesc和 CU 持久化状态的一致,在事务提交、CUDesc对应事务日志持久化前,会先行强制刷盘(Fsync),来确保事务改动的持久化。

由于数据库主备实例的同步也依赖事务日志,而 CU 文件并不包含在事务日志内,因此在与列存储同步时,主备实例之间除去正常的日志通道外,还有连接的数据通道,用于传输列存储文件。CUDesc的改动会通过日志进行同步,而 CU 文件则会被直接通过数据通道传输到备机实例,并通过 BCM(bitchangemap)文件来记录主备实例之间文件的同步状态。 

openGauss存储技术(三)——列存储引擎相关推荐

  1. 存储结构分四类:顺序存储、链接存储、索引存储 和 散列存储

    存储结构分四类:顺序存储.链接存储.索引存储 和 散列存储. 顺序结构和链接结构适用在内存结构中. 顺序表每个单元都是按物理顺序排列的,如果你想访问那个单元你可以根据提供的指针等直接访问到需要的东西, ...

  2. 网络存储技术:DAS存储、NAS存储和SAN存储

    目录 一.直连式存储(DAS) 二.网络直连式存储(NAS) 三.存储网络(SAN) 四.NAS与SAN的区别 网络存储技术(Network Storage Technologies)基于标准网络协议 ...

  3. 存储技术与智能存储组件

    前言 信息在网络中的传输,需要借助数据作为载体,那么信息和数据的关系是什么?数据存储的作用是什么? 一.数据与信息 什么是数据 SNIA(Stirage Networking lndustry Ass ...

  4. 海量结构化数据存储技术揭秘:Tablestore存储和索引引擎详解

    前言 表格存储Tablestore是阿里云自研的面向海量结构化数据存储的Serverless NoSQL多模型数据库.Tablestore在阿里云官网上有各种文档介绍,也发布了很多场景案例文章,这些文 ...

  5. 利用ISCSI存储技术构建IP存储网络(概念篇)

    一.iSCSI的概念iSCSI是一种在Internet协议上,特别是以太网上进行数据块传输的标准,它是一种基于IP Storage理论的新型存储技术,该技术是将存储行业广泛应用的SCSI接口技术与IP ...

  6. 行存储索引改换成列存储索引_列存储索引增强功能–数据压缩,估计和节省

    行存储索引改换成列存储索引 Data compression is required to reduce database storage size as well as improving perf ...

  7. 数据结构 || 二维数组按行存储和按列存储

    问题描述: 设有数组A[n,m],数组的每个元素长度为3字节,n的值为1-8,m的值为1-10,数组从内存收地址BA开始顺序存放,请分别用列存储方式和行存储方式求A[5,8]的存储首地址为多少? 解题 ...

  8. 索引存储和散列存储(哈希)的区别吧

    两者都一定的相似性,可以加快检索速度,是一种存储结构. 但是索引的方式相当于给一个本书加一个目录,这样子就可以快速找到每一个章节.好处就是增加检索速度,坏处就是增加了书的厚度,而且如果改变了书的内容, ...

  9. 持久化存储技术之SharedPreferences存储

    SharedPreferences是使用键值对的方式来存储数据的.这样在读取数据的时候就可以通过这个键把对应的值取出来. 优点:SharedPreferences是很轻量级的应用,使用起来也很方便.简 ...

  10. 持久化存储技术之本地存储

    数据持久化是指:将那些内存中(即程序中)的瞬时数据保存到存储设备中. 一.文件存储:把所有数据原封不动地保存到文件中. 使用范围:存储一些简单的文本数据或二进制数据.如果想要存储一些复杂的数据,就需要 ...

最新文章

  1. ICRA 2021| SKD:基于显着性估计的点云关键点检测
  2. LeetCode Longest Absolute File Path(栈和前缀和解法)
  3. [笔记]ndarray切片(python)
  4. 微软Azure已开始支持hadoop--大数据云计算
  5. vb.net webclient 网络目录是否存在_牛眼IPO | 怡合达IPO申请待审核 产品目录被指涉嫌侵权...
  6. 算法—2,记一个自己的算法题 计算数字k在0到n中的出现的次数,k可能是0~9的一个值
  7. xtrbackup更换数据库_XtraBackup 备份还原 MySQL 数据库
  8. java集合清空_java 集合删除数据
  9. weka使用训练集分类测试集_Giao 13C NMR计算分类训练集提高结构归属的准确性和可靠性...
  10. FMX控件演示(FireMonkey ControlsDemo)
  11. python股票收益率协方差_[Python]如何利用TuShare计算各股票间的相关性和协方差
  12. Win11打印机状态错误怎么解决
  13. 百度95后程序员删库跑路,因工作变动和对领导不满,已被民警抓获
  14. 用钩子(Hook)屏蔽特殊键
  15. C#数据Encrypt加密Encrypt解密的算法使用
  16. android系统添加内置APP(自带.so)
  17. krpano 运算符
  18. ctf-STEGA-图穷匕见
  19. java编写奇数偶数,java基础奇数偶数判断
  20. proteus 蜂鸣器系列

热门文章

  1. 小鼠IV型胶原蛋白稀释原理
  2. 1018 锤子剪刀布 (20)(20 分)
  3. html+css美图卡片小练习(未用浮动)
  4. Allegro转PADS,导入ASC时提示 规则的值超出范围
  5. 【Python】类与对象:封装/继承/多态
  6. 安卓多点触控之getAction()和getActionMasked()的区别
  7. shopex木马清除 DNS劫持清除
  8. 容易被忽视的提高邮件营销ROI的技巧
  9. USB2.0差分线设计
  10. 【论文阅读】LUPerson-NL