概述
为了严谨起见,在正式内容之前,先把OLAP多维语义模型是什么说明一下。
先说OLAP(Online Analytical Processing),它是和OLTP相对的概念,关于这两个概念的详细解释网上有很多。严格的来说OLAP与多维数据没有必然的联系,基于关系模型、图模型、时序模型或者其他数据模型的在线分析都可以叫做OLAP,只不过是因为大多数OLAP系统都采用了多维建模的思路,所以OLAP与多维数据结构就联系到了一起。
再说一下语义模型,以MySQL和Hive为例,二者的底层数据结构分别是B+Tree和HDFS,但是它们提供的SQL并没有与B+Tree和HDFS相关的概念,而是基于表和字段的这种关系型结构,所以可以认为MySQL和Hive在语义层提供了关系型的数据模型。
我们这里所说的OLAP多维语义模型指的是,一个OLAP系统无论其底层的存储结构如何(星型结构、雪花结构、宽表、稀疏维与密集维,亦或是其他结构),如果它具有在逻辑层面精确描述纯粹的多维模型的能力(通过SQL、MDX、编程接口,亦或是其他方式),则可以认为这个OLAP系统能够支持多维语义模型。
后续在介绍各种概念的同时会有实操环节,我们将使用Docker运行一个OLAP多维数据库,所以你要准备一个Docker环境。
现在开始正式内容。与OLAP多维语义模型相关的全部概念都列在了下表中,你现在只需要大概看一下即可,如果完全不懂也没关系,后续会有每个概念的详细说明。
概念 | 说明 | |
---|---|---|
维度 | Dimension | 维度就是业务角度,通常用于对数据进行分类和分组,以便更好地进行分析。 |
成员 | Member | 成员是维度中的具体元素,表示该维度下的一个特定数据点。 |
多维数据集 | Cube | 多维数据集通过关联多个维度形成逻辑上的N维立方体结构,它提供对业务数据进行多维度分析的能力。 |
度量 | Measure | 度量是在多维数据集中被分析的量化数值,代表业务过程中的关键指标,如销售额、利润等。 |
集合 | Set | 集合由若干个元组组成,集合通常被用于定义多维查询结果中维度的具体展现信息。 |
元组 | Tuple | 元组由若干个维度成员组成,一个元组是多维数据集中的一个数据点,表示一个数据组合。 |
级别 | Level | 级别是层次结构中的一个特定层,代表成员的不同粒度或详细程度,如时间维度中的年、季度、月等级别。 |
层次结构 | Hierarchy | 维度内部的成员之间具有父子级关系,层次结构就是根据成员之间的父子关系形成的一个树状结构。 |
维度角色 | Dimension Role | 当一个维度被某个Cube关联多次时,这个维度会在这个Cube上扮演不同的角色。 |
准备OLAP环境
你需要准备一台安装了Docker引擎的服务器,然后执行下面的命令,这个命令会启动一个运行了OLAP多维数据库服务的Docker容器。
$ docker run -d -p 8760:8760 --name euclidolap euclidolap/euclidolap:v0.1.8
我们将通过Python交互式编程方式来操作这个OLAP数据库,通过你本地的Python环境来连接容器内的OLAP数据库。
通过本地Python连接OLAP数据库
执行下面的命令,使用pip安装EuclidOLAP的Python包以及requests包。
$ pip install pyolap && pip install requests
进入本地Python交互式环境,然后将下面的代码拷贝进去执行,如果没有报错则表示一切正常。
# 导入pyolap包的euclidolap模块 from pyolap import euclidolap # 连接至euclidolap多维数据库服务 # IP地址要修改为你的运行docker容器的服务器IP !!! # IP地址要修改为你的运行docker容器的服务器IP !!! # IP地址要修改为你的运行docker容器的服务器IP !!! olap_ctx = euclidolap. OlapContext( "192.168.66.8", 8760) # 关闭连接 olap_ctx. close()
维度(Dimension)
维度是OLAP多维模型中最为重要的概念之一,它表示某种特定的业务角度,例如对一个电商平台的营业数据进行分析,可能会通过日期、地区、商品、支付方式等业务角度。
在建设数据仓库系统时,通常会采用星型表结构形式,你可以认为OLAP多维语义模型的维度对象就是一个星型结构中的维度表。
我们会从头建立一个多维数据集模型,然后在此模型上进行数据分析,这个Cube是一个电商平台的营业数据模型,它将会关联日期、地区、商品、支付方式四个维度,运行下面的Python代码创建这四个维度对象。
from pyolap import euclidolap # 如果使用容器内Python环境,IP地址填写 127.0.0.1 !!! # 如果使用本地Python,IP地址要修改为你的运行docker容器的服务器IP !!! olap_ctx = euclidolap. OlapContext( "192.168.66.8", 8760) # create_dimensions方法将返回一个list,其内部元素为Dimension类的实例 dimensions = olap_ctx. create_dimensions( "日期", "地区", "商品", "支付方式") for dimension in dimensions: print( f"{ type( dimension)} - [{ dimension. name} ]维度已被创建") olap_ctx. close()
成员(Member)
既然维度可以被看做是维度表,那么成员就可以被看做是维度表中的记录。
上一步中已经创建了四个维度对象,相当于建立了四张维度表,但还是空表,接下来我们建立这四个维度所对应的成员,相当于在维度表中插入记录。
from pyolap import euclidolap date_members_info = [ [ "2023", "Q1", "M1"], [ "2023", "Q1", "M2"], [ "2023", "Q1", "M3"], [ "2023", "Q2", "M4"], [ "2023", "Q2", "M5"], [ "2023", "Q2", "M6"], [ "2023", "Q3", "M7"], [ "2023", "Q3", "M8"], [ "2023", "Q3", "M9"], [ "2023", "Q4", "M10"], [ "2023", "Q4", "M11"], [ "2023", "Q4", "M12"] ] region_members_info = [ "北京", "天津", "上海", "重庆", "广州", "深圳", "杭州", "苏州"] goods_members_info = [ [ "游戏机", "PS"], [ "游戏机", "XBOX"], [ "游戏机", "Switch"], [ "体育用品", "山地自行车"], [ "体育用品", "皮划艇"], [ "体育用品", "足球"] ] pay_members_info = [ "信用卡", "微信", "支付宝"] # 如果使用容器内Python环境,IP地址填写 127.0.0.1 !!! # 如果使用本地Python,IP地址要修改为你的运行docker容器的服务器IP !!! olap_ctx = euclidolap. OlapContext( "192.168.66.8", 8760) date_dimension = olap_ctx. get_dimension_by_name( "日期") date_dimension. create_members( date_members_info) region_dimension = olap_ctx. get_dimension_by_name( "地区") region_dimension. create_members( region_members_info) goods_dimension = olap_ctx. get_dimension_by_name( "商品") goods_dimension. create_members( goods_members_info) pay_dimension = olap_ctx. get_dimension_by_name( "支付方式") pay_dimension. create_members( pay_members_info) olap_ctx. close()
维度下的成员会依据其之间的父子级关系形成一个树状结构,以商品维度为例,它呈现以下结构。
如图所示,商品维度成员形成树形结构以一个ROOT维度成员为根,这个ROOT是在创建商品维度对象后系统自动创建的,我们所创建的商品维度成员都挂在这个ROOT下面。
多维数据集(Cube)与度量(Measure)
在前两个步骤中我们已经创建了维度,并且为每个维度添加了维度成员,这两部操作等同于创建了维度表并为维度表添加数据。
接下来要创建一个Cube对象,这相当于创建事实数据表。
from pyolap import euclidolap # 如果使用容器内Python环境,IP地址填写 127.0.0.1 !!! # 如果使用本地Python,IP地址要修改为你的运行docker容器的服务器IP !!! olap_ctx = euclidolap. OlapContext( "192.168.66.8", 8760) date_dimension = olap_ctx. get_dimension_by_name( "日期") region_dimension = olap_ctx. get_dimension_by_name( "地区") goods_dimension = olap_ctx. get_dimension_by_name( "商品") pay_dimension = olap_ctx. get_dimension_by_name( "支付方式") cube = olap_ctx. build_cube( "电商销售模型", [ date_dimension, region_dimension, goods_dimension, pay_dimension], [ "销售额", "销售数量"]) olap_ctx. close()
在调用olap_ctx的build_cube方法时传入了三个参数,第一个是多维数据集的名称,第二个是此Cube所关联的维度的列表,第三个参数列表指定了这个Cube具有两个度量:销售额和销售数量,这相当于创建的事实数据表具有两个类型为double的度量字段。
到目前为止,我们创建了维度(Dimension)和维度成员(Member),然后构建了多维数据集(Cube)并且指定了度量(Measure),这一波操作就等同于创建了ROLAP星型模型的维度表和事实表,只不过事实表还是空的,当我们向事实表中插入一些数据后就可以进行多维分析了。
执行下面的Python代码,为Cube添加一些度量数据,这相当于在ROLAP事实表中插入了一些数据。
import requests from pyolap import euclidolap url = "https://sysbase.oss-cn-beijing.aliyuncs.com/电商销售模型度量数据.txt" response = requests. get( url) response. encoding = "UTF8" # 如果使用容器内Python环境,IP地址填写 127.0.0.1 !!! # 如果使用本地Python,IP地址要修改为你的运行docker容器的服务器IP !!! olap_ctx = euclidolap. OlapContext( "192.168.66.8", 8760) olap_ctx. execute( response. text) olap_ctx. close()
至此为止,我们已经创建完成了一个完整的多维数据集模型,回顾一下我们都做了什么。
-
首先,创建了维度,这相当于创建了ROLAP星型模型的维度表;
-
第二步创建了维度成员,这相当于在维度表中添加了记录;
-
第三步构建了Cube并指定了度量,这相当于创建了事实数据表;
-
第四步为Cube添加了度量数据,这相当于在事实表中插入了度量记录。
构建模型的工作已经完毕,在介绍其他概念之前,先基于这个Cube执行几个典型的多维查询。
多维数据分析
首先查询一下2023年每个季度的销售额和销售数量数据,执行下面的python代码:
from pyolap import euclidolap from pyolap. euclidolap import OLAPQueryBuilder # 如果使用容器内Python环境,IP地址填写 127.0.0.1 !!! # 如果使用本地Python,IP地址要修改为你的运行docker容器的服务器IP !!! olap_ctx = euclidolap. OlapContext( "192.168.66.8", 8760) query_builder = OLAPQueryBuilder()\ . from_cube( "电商销售模型")\ . set_rows( "[Measures].members()")\ . set_columns( "[日期].[2023].children()") result = olap_ctx. query( query_builder) print( result) olap_ctx. close()
由于没有在地区、商品和支付方式三个维度上进行限定,所以返回的数据在这个三个维度上自动聚合。