一、背景
大数据可以分成存储、计算、应用几个部分,目前国内的公司离线数仓的存储都是保存在hdfs和hive之上的;计算engine包括Spark,Flink,计算engine的作用在于能够根据用户的意图计算数据,然后将计算的结果导回到数据仓库;根据不同的粒度的聚合和计算,我们通常又将数据仓库分为几层,从底向上大致可以分成 操作数据层、公共维度模型层(包括明细汇总层、宽表模型层),应用层;
但是无论如何分层,如何保证数据的一致性;都是在保证数据更利于数据工程师使用,并不能直接打通到商分、甚至业务的同学挖掘数据的价值;
为了使商分、业务、甚至一个刚来的小白用户使用数据,我们需要打造一个更好用的用户取数平台;基于指标和维度的取数系统;下面将记录打造这系统的需要的一些必要步骤、知识;
二、指标维度体系的搭建
通常一般的公司都会有一个单独的系统管理指标维度体系,称之为元数据系统;顾名思义,就是描述数仓表、指标、维度的一个系统;通常情况下,都是数据PM、数据开发人员维护;元数据系统是开发一个根据指标、维度获取数据系统的前提;毕竟连指标、维度都没有,如何获取数据呢?
这个系统主要的目的是记录,当前有哪些表,表里有什么样的字段,字段和字段之间关系(比如级联维度),字段是指标还是维度,指标的含义(比如一线城市商家订单量)、维度和含义(比如商家维度);
通常情况下,我们将指标、维度、数据表称之为一个实体,一个实体一般都包括技术信息,业务信息;这些技术信息一般来自于数据RD的配置(规范性做得好的,可能部分可以自动生成),然后审核;业务信息一般来自于数据PM的配置,然后需要审核;
总之,元数据系统便是维护指标、维度、表模之间的关系;关系的种类有很多,比如:指标主题,维度主题,级联维度,指标、维度的技术信息、业务信息,事实表信息,维度表模型信息;(ps:若是以后有时间,再深入写一遍关于元数据系统的文章, 其理论知识可以参考阿里的《大数据之路》)
三、系统设计
3.1 系统整体架构图
如上图所示,系统整体系统可以分为、前端层,服务层,存储层;
- 前端层,主要包括一些方便用户操作的功能,比如:根据当前用户的已选指标、已选维度展示可选指标,可选维度,新建查询; 查看历史执行记录,提交查询等;
- 服务层,服务层是系统核心;主要包含三个模块;分别是元素剧管理、任务管理、查询流程管理;
- 存储层,数据仓库;
3.2 生产-消费关系图
如上图所示,系统与用户的交互图;用户主要有两类,生产者是数据开发人员,消费者是商分,业务等分析人员;首先数据开发人员生产表,并且将元数据信息注册到元数据系统;然后元数据模块同步元数据系统的中指标、维度、维表、
事实表等信息,并且根据这些指标、维度、事实表、维度表的关系,进行部分数据构建,方便被其他模块使用;
随后,数据消费者便可以在前端页面中查看增加的指标、维度;根据用户选择的指标、维度(其实一般还有一些其他的配置,比如常用的同环比,占比,指标分段,维度分组等,都可以实现),提交查询;查询服务模块接收到用户的查询,会依次进行:
- 逻辑构建(名字高大上,ps装一下,其实就是从元数据中选择可以查询出当前指标、维度的模型)
- 优化(从备选第1步的结果中,找出查询代价最小的模型,从不同的模型中查询,join的个数,表的字段等都不一样,所以查询的代价也不一样)
- SQL生成(根据需要查询的DIM,INDEX,衍生指标,衍生维度,拼接成SQL,提交给底层数据源查询,一般情况下在计算衍生维度和指标时,能下推的尽量下推,可以减少当前系统的计算量)
- 查询提交(使用Hive客户端提交查询SQL,获取数据)
3.3 元数据、图关系管理模块
元数据管理系统的目标是数据标准化,统一化;但是其中的元数据是零散的;如果元数据要被使用,还需要按照一定逻辑,在元数据、图关系模块中,对这些元数据进行组织;我们可以称这一过程为元数据的构建过程; 元数据构建过程的主要逻辑就是以根据物理事实表为起点,根据指标和维度进行扩展,扩展的结果可以视为逻辑上一张表,叫做“逻辑宽表”;注意“逻辑宽表”不是真实存在的,它下面有一张事实表和多张维度表联合组成;
- 维度扩展:若逻辑宽表是星型/雪花模型,我们根据事实表,维度表之间的绑定关系,对事实表进行逻辑上的展开;
- 指标扩展:通过原始指标 + 维度的某种计算方式,衍生出新的指标;常见的如四则计算指标,维度分组,指标分段等,扩展出新的指标;(ps:其实也可扩展出新的维度)
以上便展示逻辑宽表的扩展过程,其中订单事实表通过商家维表扩展得到商家维度表上的维度的属性,然后继续扩展商家品牌维度表获得商家品牌维度的属性;同样也可以在日期维度上进行扩展,得到日期维度;此过程叫做维度扩展; 此外,I类型商家数便是特殊类型的指标商家数,便是维度和指标结合后的衍生指标,是一种指标扩展;当然通过公式也可以扩展出新的指标,例如:
通过两种方式的扩展,我们便得到了一张逻辑宽表;逻辑宽表中的字段包含:
- 原事实表中的字段
- 维度表中扩展的属性
- 虚拟字段(计算字段等)
由此我们可以基于一张事实表,扩展出一张逻辑宽表;其实在元数据处理过程中,我们还可以更近一步;基于多张逻辑宽表的相同维度,我们还可以构建星座模型;
3.4 逻辑构建
通过元数据的构建,我们得到了逻辑宽表,目的是加强指标和维度与逻辑宽表的联系,方便的快速的根据指标/维度找模型,根据模型找指标/维度,进而可以建立指标与指标,维度与维度,指标与维度之间的关系;“逻辑构建“构建的目的便是根据输入的指标、维度找到那些逻辑表可以支撑查询;更简单来说就是满足,输入的维度必须全被逻辑宽表包含,输入的指标至少有一个被逻辑宽表包含;
举个例子,假设当前通过元数据的四张事实表F1,F2,F3,F4构建出了四个逻辑宽表T1,T2,T3,T4,它们拥有的指标维度如下表所示:
| 逻辑宽表 | 维度 | 指标 |
|---|---|---|
| T1 | d0, d1 | i1, i2 |
| T2 | d0 | i3, i4 |
| T3 | d0, d2 | i1, i3 |
| T4 | d1, d3 | i1 |
假设用户输入的指标是i1,i3; 维度是d0; 那么,逻辑构建的步骤主要包含以下步骤:
3.4.1 建立指标、维度到逻辑宽表的索引;
指标索引:
| 指标 | 逻辑宽表(维度) |
|---|---|
| i1 | T1(d0,d1);T3(d0,d2);T4(d1,d3) |
| i2 | T1(d0,d1) |
| i3 | T2(d0);T3(d0,d2) |
| i4 | T2(d0) |
维度索引:
| 维度 | 逻辑宽表 |
|---|---|
| d0 | T1;T2;T3 |
| d1 | T1;T4 |
| d2 | T3 |
| d3 | T4 |
3.4.2 指标维度过滤
根据用户指标在指标-逻辑宽表索引中过滤满足条件的模型,并且要求过滤出来的模型包含用户选择的维度;根据指标i1,i3过滤如下:
| 指标 | 逻辑宽表(维度) |
|---|---|
| i1 | T1(d0,d1);T3(d0,d2);T4(d1,d3) |
| i3 | T2(d0);T3(d0,d2) |
然后要求逻辑宽表中支持d0维度,所以去掉T4(d1,d3),得到结果如下:
| 指标 | 逻辑宽表(维度) |
|---|---|
| i1 | T1(d0,d1);T3(d0,d2); |
| i3 | T2(d0);T3(d0,d2) |
3.4.3 对索引的结果进行笛卡尔积
| 指标 | 逻辑宽表 |
|---|---|
| i1&i3 | T1&T2;T1&T3;T3&T2;T3&T3 |
这里看到 T1&T2;T1&T3;T3&T2;T3&T3 四种组合都可以查询出结果,但是T1&T3,T2&T3,T3中,明显使用T3表的代价更少;所以最终的候选包括:T1&T2;T3两种候选可以支持本次查询;
3.5 优化
一般查询计划的优化都可以分为ROP(Rule based Optimize)和COP(Cost based Optimize);一般来说基于规则的优化更加简单直接,基于代价的优化往往更复杂,但效果往往会更好;这里作为工具系统,我们应该考虑到哪些是我们这一层需要优化的,哪些是可以移交给下层数据源优化的(比如Hive,Clickhouse)?在这里我们需要优化就是选择从哪一个逻辑宽表查询数据,至于选择之后生成SQL优化,应该移交给下层数据源优化;
3.5.1 RBO
针对基于规则的优化,我们可以从以下几个维度出发去设计:
- 优先选择Join粒度更少的组合;特别是事实表级别的Join,通常都会很消耗性能;由于一个事实表可以扩展成一个逻辑宽表,所以也可以认为是选择逻辑宽表更少的组合;比如T3优于T1&T2
- 优先选择时间汇总粒度更高的表,汇总粒度更高的表,一般记录通常少于汇总粒度低表;
- 优先选择维度更少的表,在雪花模型和星型模型中,一般维度更多意味着Join的维表也更多,Join通常是计算消耗性能最多的地方;
3.5.2 CBO
针对基于代价的优化,针对历史的查询,我们需要记录每一个查询所消耗的时间,然后基于泊松分布,计算一个加权值,在选择逻辑模型的时候可以加上这个权重;鉴于笔者对于算法这块了解不甚深入,所以对于当前这块还没有进行过具体的实践,欢迎各位读者交流;
3.6 SQL生成
SQL的生成可以分为两部分,其一是逻辑宽表部分的生成;根据用户选择指标维度,推导出在逻辑宽表中需要使用维度表;根据逻辑模型中记录的Join关系,将事实表和维度表Join起来;其二是根据外部select字段,groupBy字段,where字段,having字段等 from 事实表Join维度表 生成最终的SQL;SQL用于提交到Hive/Clickhouse等底层数据源查询数据;
3.7 数据一致性问题
问题的来源,因为在建设数据仓库的过程中,我们通常会分各个不同的主题去建立不同层次的数据表,这些数据表之间的关系本身就构成了一个巨大复杂的网络;所以,有可能我们需要查询指标和维度可以通过不同的逻辑模型组合查询出来,那当两个表查询出来的数据不一致时,便产生了数据不一致的问题; 目前:还没有一个完美的方法解决该问题;但是针对这个问题提出了一些建设性的建议: 1.针对不同的逻辑模型,可以设置不同的“可信分数”,比如当用户怀疑本次查询数据是否存在问题的时候,可以再一次提交查询,我们将从“可信分数”最高的模型查询数据与当前对比;从而验证数据是否正确; 2.针对重要指标,如果可以从多个逻辑模型下查询出来,那么可以选择在空闲时刻,进行查询自动对比,发现问题及时告警;
已完结......