发布时间:2022-05-30 | 阅读:
深入浅出话DB
✦
在第二篇文章中,我们分享了柏睿数据RapidsDB的行列混存,您是否想进一步探索行存和列存的技术细节?他们又有哪些特色?别急,这就给大家介绍。第三回,数据存储,Here we go! ✦ ✦ 行存储通常用于高并发在线事务处理和事务分析混合场景。需求明确的是,RapidsDB的行存储都是存储在物理内存里,所有行存储表的总组合大小受集群中叶节点上可用物理内存总量的限制。为此,查询执行保留合理数量的物理内存(例如 20%)非常重要。行存储表的有效总容量会降低可用内存容量。 对于执行频繁的单行查找和少量插入、更新和删除的应用程序,行存储的性能比列存储高效得多。一般来说,由于行存储支持更多种类的工作负载,此时它是一个很好的使用选择。 RapidsDB的行存(内存)表可以指定一个分布键和一个或多个索引,这些分布键和索引是可选的,同时支持用户强制唯一的主键。我们可以做个使用小测试,先建一张行存表: 这里定义了一个分布键来控制数据分布,并通过字段ProductId来定义它。因为在一个或多个高基数列(极少发生重复数据)上,数据通常分布更均匀,还可以规避数据偏斜。 如果没有定义主键,则通过缺少的分布键或者定义空的分布键 SHARD KEY() 来随机分布数据。 行存表索引 具有多个索引的行存储通过多个不同的键或“访问路径”快速检索到一行或几行数据。RapidsDB行存储能够沿多个不同的访问路径提供极快的查找,并且查询之间的响应时间差异极小。 以下语句创建了一个products表,其中包含索引Price和Color,以及上的唯一(主)键ProductId: 注意:主键必须包含分布键中的所有列,这样就可以通过只查看单个分布区域中的数据来有效的检索它们。只创建主键而不指定分布键的话,系统会自动将通过主键进行分布。 平时可以单独通过语句CREATE INDEX在行式表中创建索引。 行式表持久性 需要声明的是,行存储的数据是完全持久的!!! 行式表的更新是在事务中完成的。持久性则是使用内存中数据的定期快照和预写日志来为行存储实现的。这两者都存储在文件系统中,以使它们永久存在。如果RapidsDB节点重新启动,它的所有行存储数据将从快照和日志中恢复,并且行存储的内存状态将被重建。 大型数据集上的OLTP操作 对于大型表上的OLTP操作,在使用行存储时,配置足够的内存以及足够的服务器的总成本可能会成为一些用户非常关心的问题。如果是这种情况,并且应用程序不需要最快的行查找时间,可以考虑使用列式表,在最常用的查找键列上使用散列索引。或者,对于具有许多空值的宽表,可以通过使用具有稀疏数据压缩的行式表来降低总拥有成本。 上段已经提到了列存表,现在马上给大家介绍列存储的优化加速原理: 聚集列存储索引:RapidsDB会定义一个聚集列存储索引,它代表完整的表结构并且是其主存储。 列存储键列:这是创建列存储索引时,会将一列或多列定义为列存储索引的键列。列存储中的数据按键列顺序存储。因此,选择一个好的列存储键可以显著提高性能。 行段:行段表示列存储索引中的一组逻辑行,它有利于用户日常的增删改等数据操作。RapidsDB将每个行段的元数据存储在内存中,其中包括该段的总行数和跟踪哪些行已被删除的位掩码。 列段:每个行段包含表中每一列的列段。列段是列存储表的存储单元,包含行段中特定列的所有值。列段中的值始终以相同的逻辑顺序存储在同一行段内的列段之间。RapidsDB将每个列段的元数据存储在内存中,其中包括段中包含的最小值和最大值。此元数据在查询执行时用于确定段是否可能匹配过滤器,该过程称为段消除。 已排序的行段组:已排序的行段组表示在列存储键列上一起排序的一组行段。这意味着,在已排序的行段组中,不会有行段与构成列存储索引键的列的值范围重叠。在对表运行INSERT、LOAD或UPDATE查询后创建更多段时,会形成新的段组。 用个例子来说明在日常查询中,RapidsDB的列存表是怎么样实现高性能的,下面是在Products表中使用Price列作为关键字的RapidsDB列式表索引的示例: 注:列段通常包含几十万行,在本例中,为了可读性,段的大小为5行。 已排序的行段组1组: 行段组2组: 行段组3组: 注:×N表示该值被重复N次 对具有列式表索引的表查询可以利用列式表索引的五个特征: 所有查询都能够利用以下情形:只需要扫描包含查询中引用的列的列段,以及列存储的压缩导致需要被扫描的数据较少。以上表为例,查询SELECT SUM(Qty) FROM Products;,将只需要扫描三个Qty列段,并且由于压缩,每个列段只包含了一个值。 一些查询可以简单地通过读取查询中引用的列段的内存元数据来执行。例如,查询SELECT COUNT(*) FROM Products; 将只需要读取行计数并删除所有行段的位掩码来产生结果,完全消除了从磁盘读取列段的需要。如果没有对列段执行删除操作,使用MIN或MAX聚合的查询可以消除从磁盘读取列段的需要。