65.9K
CodeProject 正在变化。 阅读更多。
Home

改善多维分析后端性能的四种方法

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2018年1月10日

CPOL

9分钟阅读

viewsIcon

4621

有四种方法可以加速OLAP分析。

多维分析,通常称为OLAP,是一种交互式数据分析过程,它对数据立方体执行旋转、切片和切块、下钻等操作。其后端计算的结构很简单,如下面的SQL所示:

SELECT D,..., SUM(M), ... FROM C WHERE D'=d' AND ... GROUP BY D,...

该语句按某些维度聚合某些度量。C 是一个数据立方体,D,... 表示要选择的维度,M,... 表示要聚合的度量。除了SUM,我们还可以使用其他聚合函数。D' 是切片维度。切块操作的标准是D IN (d,...)。我们还可以在WHERE子句中定义度量规则,以选择特定范围内的值。

OLAP分析需要即时响应,因此高性能至关重要。尽管语句结构简单,但在处理大型数据立方体时可能会涉及大量计算。在我们找到优化它们的方法之前,分析会很慢。以下列出了多维分析中提高后端性能的常见方法。

预聚合

预聚合是早期OLAP产品普遍采用的一种方法,它通过增加存储空间来换取效率。该方法是预先计算(在GROUP BY子句中定义的)某些或所有维度的聚合值(在SELECT查询中定义的度量),然后将它们存储起来。这些中间结果可以直接用于后续计算,或生成新的计算。这样可以大大提高性能。

预聚合结果占用的空间很大。通常,有十几个甚至几十个维度,每个维度的值范围从个位数到两位数。简单的计算表明,预聚合结果将比原始数据立方体大几倍到几十倍(即(k1+1)*(k2+1)*...k1*k2*...的比率,同时考虑了各种类型的聚合函数)。虽然为了获得即时响应,数据立方体不会太大,但数据量大几十倍是不可行的。

一种折衷的方法是仅按部分维度计算聚合值。由于OLAP界面中只显示少数几个分组维度(在GROUP BY子句中定义),我们可以按这m个维度进行聚合。如果m的值不大于5,则存储消耗将在合理范围内,并且大多数用户操作都能得到快速响应。

然而,部分聚合无法处理其他维度上的切片条件。但下钻是基于切片的。更糟糕的是,即使是全包含的聚合也无法处理度量上的条件(例如,获取销售额大于1000元的数据),这在多维分析中并不少见。聚合函数也可能包含条件(例如,只合计小于100元的成本)。在所有这些情况下,预聚合结果都无用。

预聚合只能处理最常见的情况,在所有类型的多维分析场景中占少数。大多数情况下仍然需要完全遍历。

基于段的并行处理

本质上,多维分析是数据过滤和分组,这些很容易并行执行。步骤是将数据分成多个段,分别处理,然后收集处理结果进行聚合,在此过程中子任务是相互独立的。从单机上的多线程处理,到多节点集群计算,再到两者的结合,每种实现都不难。

多维分析的结果是用于查看的。但我们肉眼能看到的数据远小于现代计算机内存能容纳的数据。对于可以轻松加载到内存中的数据集,无需在内存和磁盘之间进行交换。编程相对容易,性能也很高。计算过程中生成的大型数据集将直接报告给界面,然后计算将中止。

根据我们的测试,如果多线程处理中的所有子任务将它们的结果合并到同一个结果集中,由于多个线程使用单一资源时的同步操作,性能可能会受到严重影响,尽管表面上看,使用共享的最终数据集可以减少内存占用。

更多的线程不一定更好。当线程数超过CPU核心数时,它会变得无效。对于存储在外部存储中的数据,需要进行测试以获得多线程处理的实际结果,因为硬盘的并发能力(通常小于CPU核心数)需要考虑在内。 

根据记录数和标记每个段的结束来划分静态数据很容易。但要均匀划分动态数据则很麻烦。未来文章将进一步讨论。

对于单个计算任务,并行处理可以带来几倍的性能提升。由于OLAP操作基本上是并发事务,即使在用户数量很少的情况下,性能提升也可能被抵消。

需要更好的方法。

排序索引

不带切片的聚合操作总是涉及整个数据立方体。除了预聚合,我们几乎没有什么可以减少计算的。但通过切片操作(下钻),如果数据立方体已经排序,则无需完全遍历。

如果我们能为维度D创建一个索引,即按特定顺序对其值进行排序,并关联其对应记录的序号,那么我们就可以快速定位满足包含维度D的切片条件的记录。这是一个简单的二分查找。无需对所有数据进行完全遍历,计算量将减少几个数量级(这也取决于D的值范围)。理论上,我们可以为每个维度创建索引。成本并不高。当涉及相对切片时,性能将大大提高。

但多字段维度D1D2的索引用处不大。它无法快速定位仅包含维度D2的切片。只有当D1D2都包含在切片中时,它才有效。在定位了具有最大值范围的维度的切片记录后,计算量已经大大减少。然后我们可以按维度遍历其他切片。

不幸的是,这种原始方法仅适用于处理内存中的数据,这允许频繁的小量访问。在大多数情况下,要处理的数据集很大,需要存储在磁盘上。但是,即使有了索引,检索大量无序记录对性能的提升也很小。只有当数据真正有序且每个切片中的记录连续存储时,性能才能明显提高。

由于每个排序维度都需要复制数据,成本相当高。

一种解决方案是创建两份数据副本:一份按维度D1, …, Dn排序,另一份按维度Dn, …, D1排序。生成的数据量是原始数据的两倍,这是可以接受的。有了这两个维度序列,切片维度总会落在前半部分,从而确保该维度切片中的数据大致连续,从而确保更好的性能提升。 

压缩列存储

处理多维分析的一个强大工具是列式存储。

在多维分析中,数据立方体通常有大量的字段(维度和度量),从几十个到几百个。但有用的字段并不多,通常只有5个或更少,如果忽略切片维度的话。由于切片可以通过索引处理,所以只需要遍历少数几个字段。

鉴于此,列式存储可以带来优势。在外部存储计算过程中,I/O操作占主导地位。因此,与减少计算量相比,减少检索数据量以提高性能更有意义。对于一个有100个字段的数据立方体,如果只检索5个字段,I/O消耗会下降到原来的1/20,从而带来数量级的性能提升。

列式存储的另一个优点是支持数据压缩。在按维度D1,…,Dn排序和存储数据时,我们发现D1在连续记录中具有相同的值;D2也是如此,在数量较少的连续记录中;依此类推,越来越少的连续记录。几乎没有Dn的连续性。考虑到不需要重复存储这些连续相同的值,我们可以存储一次并记录它们的数量。通过减少数据占用的空间,我们减少了外部存储的I/O访问,提高了性能。

使用列式存储时,我们需要注意一些问题:

由于列式存储不会减少计算量,所以在处理内存中的数据时帮助不大。但压缩存储方案有助于减少内存消耗。

列式存储会使基于段的并行处理和索引创建变得复杂。列的分割需要保持对齐。索引应同时正确引用所有列。并且当使用压缩列存储时,会更麻烦。尽管有这些繁琐的问题,但在静态数据上使用列存储通常并不困难(只要确保不会忘记处理它们)。

使用列存储会增加并发压力。当总字段数不多或需要检索大量字段时,它会失去优势。对于HDD,额外使用并行处理会进一步增加并发压力,并可能导致性能下降。它更适用于SSD,SSD支持更好的并发。

© . All rights reserved.