如何使柱状存储数据仓库更有效
#编程 #database #bigdata

许多数据仓库采用柱状存储。如果数据表中的列总数很大并且计算中涉及的列数很小,则仅在使用列存储时才需要读取所需的列,这可以减少访问量的硬盘并改进表演。尤其是当数据量非常大时,扫描和阅读硬盘的时间很大,柱状存储的优势将很明显。

因此,我们可以得出结论,只要使用柱状存储,就可以实现最佳性能?让我们看看柱状存储在哪些方面可以更有效地执行。

压缩

结构化数据的编码方法通常不是很紧凑,并且通常有一些压缩空间。通常,数据仓库会根据柱状存储压缩数据,以物理减少数据存储量,以减少阅读时间并提高性能。数据表中同一字段的数据类型通常是相同的,在某些情况下,即使值也非常接近。对于这样一批数据,通常可以获得良好的压缩率。柱状存储是将同一字段的值一起存储在一起,因此比基于行的存储更适合数据压缩。

但是,常规压缩算法不能假定数据具有某些特征,并且只能将它们视为自由字节流进行编码,有时无法获得最佳的压缩率。此外,通过高压率压缩的算法压缩的数据通常会增加CPU的运行量,并在解压缩时消耗更多的时间,而解压缩的时间甚至比数据压缩的硬盘节省的阅读时间更大。结果,损失大于收益。

如果我们事先处理数据以人为地使它们具有一定的特征,然后将这些特征与压缩算法一起使用,我们可以达到高压率并保持低CPU消耗。

一种有效的处理方法是在存储前对数据进行分类。数据表中通常有许多维数字段,例如区域,日期等。这些维度的值基本上在较小的集合范围内,当数据量较大时,将有许多重复值。如果这些列对数据进行排序,则相邻记录具有相同的值是常见的。在这种情况下,使用轻质压缩算法可以获得良好的压缩率。简而言之,我们可以直接存储列值及其重复数量,而不是多次存储相同的值,而是可以节省大量空间。

排序顺序也很重要,我们应该将具有更长字段值的列放在最初的位置。例如,有两列:区域和性别,区域价值(北京,上海等)的字符数量大于性别价值观(“男性”,“男性”,“男性” “女性”)。在这种情况下,按区域和性别顺序排序的效果比以相反顺序进行分类的效果更好。

除了排序外,我们还可以通过优化数据类型(例如将字符串和日期等)来节省空间,以将其转换为适当的数字代码。如果将区域和性别场全部转换为小整数数,则场值的长度将相同。转换后,我们应该首先将其带有更多重复值的字段。例如,性别只有两个枚举值,而该地区的枚举值相对较高,因此性别领域将有更多重复的值。在这种情况下,首先按性别进行排序通常会占据较小的空间。

开源数据计算引擎SPL中提供的柱状存储方案实现了此压缩算法。当将订购的数据附加到SPL的复合表中时,SPL将默认情况下自动执行上述方法,仅记录一次重复的值。

SPL代码用于创建有序的柱状存储复合表和执行遍历计算大致如下:

代码示例1:订购和压缩柱状存储和遍历计算

    A
1   =file("T_ordinary.ctx").open().cursor(f1,f2,f3,f4,…).sortx(f1,f2,f3)
2   >file("T.ctx").create(#f1,#f2,#f3,f4,…).append@i(A1)
3   =file("T.ctx").open().cursor().groups(…;sum(amt1),avg(amt2),max(amt3+amt4),…)

a1:创建原始数据的光标,然后按三个字段F1,F2,F3。

进行排序。

a2:创建一个新的复合表,并指定订购三个字段F1,F2,F3。将排序数据写入复合表。

a3:打开创建的新复合表,并执行分组和聚合。

以下测试表明,在SPL采用数据类型优化以及有序和压缩的柱状存储后,数据存储量减少了31%,计算性能提高了9倍以上。有关测试结果,请参见图:

Image description

并行计算

多线程并行计算可以很好地利用多个CPU的计算能力,并且是加快加速的重要手段。要执行并行计算,需要首先对数据进行分割。对于基于行的存储,分割相对简单,即根据数据量执行粗糙的平均分割,然后找到记录的结尾标记以确定分割点的位置。但是,对于柱状存储,此方法不起作用。由于柱状存储的不同列是单独存储的,因此必须分别分割它们。此外,由于字段的长度未固定,并且存在压缩数据,因此每个列的相同分段点的位置不一定属于同一记录,这将导致读取错误。

为了解决此问题,该行业通常采用阻止方案来求解柱状存储的分段同步:块内的数据存储为柱状存储;分割必须在块单位中执行;段中的平行计算不再在块内部执行。为了付诸实践,它需要首先确定每个块的数据量的大小。如果固定数据表的总数据量,并且以后没有附加数据,则很容易计算合适的块的大小。但是,数据表将与通常的新数据连续附加,这将导致如何确定块的大小。如果确定块的大小是较大的,那么当总数据量在初始阶段很小时,块的数量将相对较小,从而导致柔性分割失败,但是均匀且灵活的分割是通往的关键确定并行计算的性能。相反,如果确定块的大小为较小,则数据量增加后块的数量将变大,并且列数据将实际分为许多不连续的小块,这将导致额外的读数块之间的少量无用数据。考虑到硬盘的寻求时间,块的数量越多,问题就越严重。由于许多数据仓库或大数据平台无法解决块数量和数量之间的矛盾,因此很难充分利用并行计算来提高性能。

SPL提供了双重增量分割方案,也就是说,将固定的(物理)阻塞更改为动态(逻辑)阻塞。通过该计划,可以很好地解决上述矛盾。具体步骤是:i)为每列数据创建一个具有固定大小(例如1024个索引单元)的索引区域,每个索引单元存储一个记录的起始位置,这意味着一个记录代表一个块; ii)附加数据,直到填充所有索引单位为止; iii)重写索引区域,丢弃均匀的索引单元,然后向前移动奇数索引单元,以使索引区域的后半部分缩小,此步骤等同于将块的数量减少到512,而两个记录表示一个块; iv)重复附加数据,填充索引单元并重写索引区域的过程。随着数据量的增加,块的大小(块中的记录数)将连续加倍。应该注意的是,所有列的索引区域都应同步填充,并且在填充索引单元后应同步重写索引区域以保持它们的一致性。该方案本质上使用记录的数量作为分割基础,而不是字节数,因此即使单独进行分割,也可以同步执行每个列的分割,并且不会存在错误对准。

在动态块的单位中进行分割时,块数量保持在512和1024之间(当记录数小于512时),这可以满足灵活分割的要求。由于与每列的动态块相对应的记录数量完全相同,因此它也可以满足均匀分割的要求。因此,无论数据量如何,都可以获得良好的分割效果。可以在:。

代码示例1中生成的复合表T默认情况下采用双重增量细分方案。要在t上进行并行计算,只需将A3中的代码修改为:

=file("T.ctx").open().cursor@m().groups(…;sum(amt1),avg(amt2),max(amt3+amt4),…)

并行计算只能通过将选项@m的后缀@M进行执行。

将来附加数据时,无需再生复合表,而是打开此复合表并直接附加数据。代码大致是这样的:

> file("T.ctx").open().append@i(cs)

在此代码中,它需要确保CRSOR CS中的符合条件的数据也由三个字段F1,F2,F3订购。但是,实际上,待办事项可能不符合这种情况。要解决此问题,SPL提供了高性能解决方案,请访问:http://c.raqsoft.com/article/1645175380545有关详细信息。

搜索

柱状存储更适合遍历遍历计算,例如分组和聚合。但是对于大多数搜索任务,柱状存储将导致性能较差。当未使用索引时,即使数据已有序存储,通常的柱状存储也无法使用二进制搜索。原因与上述并行分割相同,这仍然是因为柱状存储无法确保所有列的同步,这可能会导致未对准,从而导致读取错误。在这种情况下,只能通过穿越来搜索柱状存储数据,并且性能将非常差。

为了避免横向,我们可以在柱状存储数据表上创建一个索引,但这非常麻烦。从理论上讲,如果我们想记录索引中每个字段的物理位置,则索引大小将大得多,比基于行的存储索引大得多,甚至可能与原始数据表一样大(因为每个字段都有一个物理位置,索引中的数据量与原始数据相同,只有数据类型更简单)。此外,它需要在阅读时分别在每个字段的数据区域中读取数据,但是硬盘具有最低读数单元,这将导致每列的总读数量超过基于行的存储的总读数,并且结果是搜索性能更糟。

采用了双重增量分割机制后,SPL可以根据记录的序列编号更快地在柱状存储中找到每个字段值,这允许执行二进制搜索。此外,由于我们只需要将整个记录的序列编号存储到索引中,因此索引的大小将小得多,并且与基于行的存储几乎没有差异。但是,当使用二进制搜索或索引进行搜索时,它仍然需要分别读取每个字段的数据块,并且性能仍然无法赶上基于行的存储。因此,如果您想进行极端的搜索性能,则必须使用基于行的存储。实际上,最好让程序员根据计算需求选择是否采用柱状存储。可惜的是,某些数据仓库产品采用了透明的机制,该机制不允许用户在使用基于行的存储或柱状存储方面有自己的选择,因此很难获得最佳结果。

SPL将此选择权留给开发人员,他们可以决定是否使用柱状存储以及根据实际需求使用柱状存储的哪些数据,以获得极端性能。

在前面的简介中,复合表默认使用柱状存储,还提供了基于行的存储模式,可以使用选项@R。

指定该模式

代码示例1的A2中的代码可以更改为:

=file("T_r.ctx").create@r(#f1,#f2,#f3,f4,…).append@i(A1)

这将生成一个基于行的存储复合表。借助柱状存储和基于行存储的两个复合表,程序员可以根据需要自由选择和使用它们。

对于需要遍历和搜索都需要高性能的方案,我们只能为计算时间提供存储空间。也就是说,将数据冗余两次存储,并使用柱状存储进行遍历,然后使用基于行的存储进行搜索。但是,由于需要两次存储此类共存方案中的数据,并且还需要为基于行的存储创建索引,因此所占用的整体硬盘空间将相对较大。

SPL还提供了带有值机制的索引。创建索引时,可以同时复制其他字段值。原始的复合表继续使用柱状存储进行遍历,而由于索引本身已经存储了字段值并使用了基于行的存储,因此在搜索过程中通常不再访问原始表,可以获得更好的性能。像上述共存方案一样,这种机制可以考虑到遍历和搜索的性能。此外,该机制将基于行的存储与索引结合在一起,因此它占据的空间比共存方案少。

代码示例2:带值的索引

    A
1   =file("T.ctx").open()
2   =A1.index(IDS;f1;f4,amt1,amt2)
3   =A1.icursor(f1,f4;f1==123456).fetch()
4   =A1.icursor(f4,amt2;f1>=123456 && f2<=654321)

在A2中创建索引ID时,将待办词引用的字段F4,AMT1,AMT2复制到参数中可以在索引中复制这些字段值。当将来获取目标值时,只要涉及字段在索引中,就无需读取原始表。

评论和摘要

使用柱状存储时,它只需要读取所需的列。当列的总数很大并且计算中涉及的列数很小时,此存储方法可以减少访问量的硬盘并改善性能。但是,仅使用柱状存储无法达到我们的期望,对于柱状存储数据仓库,它还需要在数据压缩,多线程并行计算和搜索计算中进行优化,以最大程度地发挥柱状存储的效果。

开源数据计算引擎SPL充分利用以有序方式存储数据的功能,并在保持低CPU消耗的前提下实现具有较高压缩率的压缩算法,并大大降低了物理存储数量并进一步提高性能。 SPL还提供了双重增量分割机制,该机制解决了柱状存储分割的问题,从而使柱状存储数据可以完全使用并行计算来提高效率。此外,SPL可以自由建立基于行的存储和柱状存储数据表,从而使开发人员可以自己选择。更重要的是,SPL提供了 index-in-with-values 机制,该机制可以同时实施高性能遍历和搜索计算。

起源:https://blog.scudata.com/how-to-make-the-columnar-storage-data-warehouse-more-efficient/
SPL源代码:https://github.com/SPLWare/esProc