写点什么

适用于即席查询(Ad-Hoc)的 OLAP 引擎

作者:Western Panda
  • 2023-11-09
    天津
  • 本文字数:4575 字

    阅读完需:约 15 分钟

适用于即席查询(Ad-Hoc)的OLAP引擎

OLAP(OnLine Analytical Processing)是一种数据处理技术,OLAP 系统通常被用来进行面向业务主题的数据分析,在大数据量情况快速响应复杂查询以及灵活的进行随机性探索性分析是 OLAP 系统的核心能力。

在 OLAP 系统中常见的数据模型为以下四种结构:


  • 星型表结构(Star Schema)

  • 雪花型表结构(Snowflake Schema)

  • 宽表结构(Wide Table)

  • 数据立方体结构(Cube)

这几种常见的结构都可以在逻辑上被理解成是多维空间中的多维立方体。



接下来介绍一个在后续的实践环节中将要用到的数据模型——Andes Online Store,这是一个电商的销售数据集合,它关联了六个维度:日期(Date)、商品(Goods)、地区(Region)、支付方式(Payment Methods)、客户类型(Customer Types)、销售渠道(Sales Channels),它有两个度量:销售额(Sales)和销售数量(Sales Count)。



对于这个数据模型,不同的人会有不同的分析需求,例如:


  • 分公司负责人重点关注所在地区的销售数据,公司决策层更关心整体的销售数据,整体数据是由各个地区的明细数据聚合而得出,不同的人关注的数据粒度不同;

  • 采购负责人会比较关心商品维度上的数据变化,营销人员关心的是客户或销售渠道维度上的数据变化,而财务人员可能会对日期维度上的数据变化更感兴趣,不同的人关注的业务角度不同。

即席查询(Ad Hoc)是用户根据自己的需求,灵活的选择查询条件,OLAP 系统根据用户输入的查询条件实时返回查询结果。OLAP 的即席查询与普通查询的不同之处就是很难对前者进行预先的优化,因为即席查询所响应的大都是随机性很强的查询请求。一个 OLAP 系统的即席查询能力越强,其应对不同用户的随机性和探索性分析的能力就越强。

EuclidOLAP是一个适用于即席查询场景的开源 OLAP 引擎,它使用 C 语言开发,支持单机运行和分布式部署。

EuclidOLAP通过以下特征来提供对即席查询场景的支持能力:


  • 实时聚合 — 对任何维度的粗粒度数据进行实时聚合运算,无需进行预计算处理;

  • 内存模式 — 数据被加载到内存中的弹性索引结构中,一亿数据量只需要 3G 内存,32G 内存便可支持十亿级数据的实时分析;

  • 多维模型 — EuclidOLAP采用 Cube 结构作为语义层模型,其完全可以等同于逻辑多维模型,并且更加贴近现实业务;

  • 复杂查询 — 使用类 SQL 语言 MDX,MDX 在语法结构上与 SQL 非常类似,但提供了比 SQL 更加强大的应对复杂查询的能力;

  • 关联查询 — 支持跨 Cube 查询,这类似于关系数据库中的 Join 操作,但EuclidOLAP跨 Cube 查询不会导致性能明显下降。

下图是EuclidOLAP单机运行时的架构图。



  1. 磁盘中数据模型的元数据部分被加载到内存中形成概要文件(Profile),profile 用来描述维度信息;

  2. 度量数据被加载到内存中的弹性索引(Elastic Index)中(如果你了解 Oracle Essbase 或 IBM Cogons,那么可以将弹性索引在逻辑上理解为动态的稀疏维索引和密集维数据块),弹性索引是EuclidOLAP实现高效实时聚合的关键;

  3. Profile 和弹性索引表示了一个 Cube 的物理存储结构,它完全等同于 MDX 语义层的逻辑多维模型;

  4. MDX 解析器接收一个 MDX 查询请求并将其解析为一个多维查询抽象语法树(AST);

  5. 对 AST 的进一步处理分为逻辑运算(Logical operation)和聚合运算(Aggregate operation)两部分;

  6. 逻辑运算将通过概要文件确定此次查询请求的数据范围,同时处理函数解析、数学运算、布尔操作等一系列与复杂逻辑相关的步骤;

  7. 聚合运算通过逻辑运算所确定的范围通过弹性索引进行实时汇总运算;

  8. 将查询结果以多维结果集(通常是一维或二维)的形式返回。

在分布式部署时,EuclidOLAP集群分为负责逻辑运算的 master 节点和负责聚合运算的 worker 节点。一个数据集市模型的维度元数据被加载到 master 节点中形成概要文件,度量数据以分片的形式存储在各个 worker 节点中,如下图所示。



接下来进入实践环节,我们先使用 Docker 运行一个EuclidOLAP服务,然后模拟各种用户根据自身的需求面向业务模型进行随机的探索式的数据分析。

在命令行窗口中执行下面的指令,运行一个EuclidOLAP服务:

docker run -d -p 8760:8760 --name euclidolap euclidolap/euclidolap:v0.1.7
复制代码

文章开始部分已经简单介绍了用于实践环节的数据模型示例 — Andes Online Store,这里我们再回顾一下这个 demo。


Date、Goods 和 Region 三个维度具有多层级结构,分别如下:

  • Date:Root(默认全部汇总)> Year > Quarter > Month > Day

  • Goods:Root(默认全部汇总)> 商品大类 > 分类 > 具体商品

  • Region:Root(默认全部汇总)> 洲 > 国家

Payment Methods、Customer Types 和 Sales Channels 三个维度是单层级结构,如下:

  • Payment Methods:Root(默认全部汇总)> 支付方式

  • Customer Types:Root(默认全部汇总)> 客户类型

  • Sales Channels:Root(默认全部汇总)> 销售渠道

接下来执行一个简单的查询,验证 EuclidOLAP 是否启动成功。

执行以下命令进入 Docker 容器:

docker exec -it euclidolap /bin/bash
复制代码

在容器中执行 olap 客户端命令行工具:

./olap-cli
复制代码

在 olap 客户端中执行下面的 MDX 查询语句:

select[Date].[2022].[Q4] on rows,[Goods].[Foodstuff] on columnsfrom [Andes Online Store];
复制代码



如果你的屏幕上显示出了相同的结果表示 EuclidOLAP 运行正常。

后面将通过一系列的随机查询来演示 EuclidOLAP 的即席查询能力。

通过任意维度分析数据

下面的 3 个 MDX 语句分别从支付方式、客户类型和销售渠道三个维度查看相应销售额和销售数量数据。对于其他没有被显式指定的维度,EuclidOLAP 将自动进行汇总。执行下面三个 MDX 并获得查询结果。

支付方式

select{[Payment Methods].[Credit card], [Payment Methods].[PayPal]} on rows,{[Measures].Sales, [Measures].[Sales Count]} on columnsfrom [Andes Online Store]where ([Date].[2022].[Q4].[M12]);
复制代码



客户类型

select{[Customer Types].[Bargain hunters], [Customer Types].[New customers]} on rows,{[Measures].Sales, [Measures].[Sales Count]} on columnsfrom [Andes Online Store]where ([Date].[2022].[Q4].[M12].[31], [Goods].[Foodstuff].[Meat]);
复制代码



销售渠道

select{[Sales Channels].[Wholesale], [Sales Channels].[Direct sales]} on rows,{[Measures].Sales, [Measures].[Sales Count]} on columnsfrom [Andes Online Store]where ([Date].[2022].[Q4].[M12].[31], [Goods].[Foodstuff].[Meat]);
复制代码



通过不同的数据粒度进行分析

下面的 3 个 MDX 语句分别从商品维度的具体商品、商品分类和商品大类三个粒度进行查询。

按具体商品查询 2022 年 1 季度销售额

select[Date].[2022].[Q1] on rows,{[Goods].[Foodstuff].[Drink].[Milk], [Goods].[Foodstuff].[Drink].[Tea]} on columnsfrom [Andes Online Store];
复制代码



按商品分类查询 2022 年 1 季度销售额

select[Date].[2022].[Q1] on rows,{[Goods].[Foodstuff].[Drink], [Goods].[Foodstuff].[Meat]} on columnsfrom [Andes Online Store];
复制代码



按商品大类查询 2022 整年的销售额

select[Date].[2022] on rows,{[Goods].[Foodstuff], [Goods].[Clothing]} on columnsfrom [Andes Online Store];
复制代码



可以在查询时将任何维度放置在任何位置

下面这条 MDX 语句返回的结果是日期与商品维度的交叉透视表,日期与商品被分别放置在列和行上:

select{[Date].[2021], [Date].[2022]} on rows,{[Goods].[Foodstuff], [Goods].[Clothing]} on columnsfrom [Andes Online Store];
复制代码



稍微修改一下上面的 MDX 就可以将日期与商品维度的位置互换:

select{[Date].[2021], [Date].[2022]} on columns,{[Goods].[Foodstuff], [Goods].[Clothing]} on rowsfrom [Andes Online Store];
复制代码



可以对维度进行交叉组合

下面这个 MDX 语句查询了各种客户类型与支付方式对应的销售数据,客户类型和支付方式维度被同时放置在行位置并进行交叉组合。

selectCrossjoin(    {[Customer Types].[Bargain hunters], [Customer Types].[New customers]},    {[Payment Methods].[Credit card], [Payment Methods].[PayPal]}) on rows,[Measures].Members on columnsfrom [Andes Online Store]where ([Date].[2022].[Q4].[M12]);
复制代码



数学运算

下面的 MDX 定义了一个新的度量 Formula_Measure,它的值是对销售数量度量进行数学运算而得出的:

with     member [Measures].[Formula_Measure]         as (([Measures].Sales + 1000) / 20 - 33000) * 0.01select[Date].[2022].[Q1] on rows,{[Measures].Sales, [Measures].[Formula_Measure]} on columnsfrom [Andes Online Store];
复制代码



跨业务模型分析

下面这个 MDX 通过 LookupCube 函数将两个 Cube 关联起来进行分析,这类似于关系数据库中的 Join 操作,但与 Join 操作不同之处在于 EuclidOLAP 的跨模型分析不会造成查询性能的显著下降。

with    member [Measures].other_sales as LookupCube("Sahara Online Store", [Measures].Sales)select[Date].[2022].[Q1] on rows,{[Measures].Sales, [Measures].[other_sales]} on columnsfrom [Andes Online Store];
复制代码



函数

下面 3 个 MDX 查询分别使用了成员函数(Member Function)、集合函数(Set Function)和数值函数(Numeric Function)。

使用 Parent 函数查询澳大利亚所在洲的 2022 年的销售数据:

select[Region].[Oceania].[Australia].Parent() on rows,([Date].[2022], [Measures].Sales) on columnsfrom [Andes Online Store];
复制代码



使用 Children 函数查询球类商品分类下的所有具体商品的销售数据:

selectChildren([Goods].[Motion].[Ball]) on rows,[Measures].Members on columnsfrom [Andes Online Store];
复制代码



使用 Avg 函数查询 2022 年各个季度销售额的平均值:

with member [Measures].AVG_SALES    as Avg(Date.currentMember().Children(), [Measures].Sales)selectDate.[2022] on rows,[Measures].AVG_SALES on columnsfrom [Andes Online Store];
复制代码



逻辑运算

下面的 MDX 使用了逻辑函数——IsLeaf,当商品维度成员为明细成员时显示其对应的销售额数据,对于非明细成员则显示一个自定义字符串。

with member [Measures].mix_num_str    as Iif(IsLeaf(CurrentMember(Date)), [Measures].Sales, "NO LEAF MEMBER")select{Date.[2022], Date.[2022].Q1.M1.[10]} on rows,[Measures].mix_num_str on columnsfrom [Andes Online Store];
复制代码



计算公式维度成员

下面的 MDX 使用了一个自定义的度量维度成员——Proportion,它的值是通过当前商品维度成员的值与其父级成员的值动态计算得出,可以使用它来查看各种具体商品占其所属商品分类的百分比。

with member [Measures].Proportion     as (Goods.CurrentMember, [Measures].Sales) / (Goods.CurrentMember.Parent, [Measures].Sales)select[Goods].[Foodstuff].[Fruits].Children() on rows,[Measures].Proportion on columnsfrom [Andes Online Store];
复制代码



自定义集合

下面的 MDX 在定义查询条件之前设置了两个自定义集合——东亚国家和北美国家,这样在查询条件中就可以直接使用自定义集合的名称。

with    set [East Asian countries] as {[Region].[Asia].[China], [Region].[Asia].[Japan], [Region].[Asia].[India]}    set [North American countries] as {[Region].[North America].[Canada], [Region].[North America].[United States of America]}selectUnion([East Asian countries], [North American countries]) on rows,[Measures].Sales on columnsfrom [Andes Online Store];
复制代码



用户头像

Western Panda

关注

还未添加个人签名 2023-10-30 加入

还未添加个人简介

评论

发布
暂无评论
适用于即席查询(Ad-Hoc)的OLAP引擎_数据库_Western Panda_InfoQ写作社区