bi系统

304 阅读8分钟

一、背景

一般随着公司业务蒸蒸日上,精细化运营会越发的重要;现在很火的一个概念叫做“数据驱动业务“,驱动的方式多种,常见的有即席查询,取数分析,报表分析等;一般对于数据分析工具都要求:数据准确性,时效性,信息完整性,分析功能多样性;本文将介绍如何设计一个满足以上特性的报表系统;

二、系统设计

2.1 系统目标

作为报表系统,从功能层面和易用性层面都对报表提出了较高的要求,具体包括:

  • 顺畅的协助流程,满足数据开发和报表开发人员之间的交互
  • 丰富的分析函数,满足业务多样性,包括各种衍生指标、维度;
  • 接入多种数据源,屏蔽底层数据源的异构性
  • 丰富的可视化组件(饼图,直方图,散点图,热力图,桑基图等)

2.2 系统概念

为更好的介绍BI工具系统设计,需先了解数据集,配置面板等概念

2.2.1 数据集

数据集是数据的定义阶段,其中的数据定义包括:数据集对应的字段信息和数据源信息;字段信息包括指标、衍生指标、维度、衍生维度等信息;数据源信息是描述数据的存放地点和方式,比如mysql的某一张表;数据集的描述如下所示: image.png 在上图中我们还可以根据元数据来源分为物理元数据、分析元数据:

  • 物理元数据:跟数据源相关,一般不会改变,比如mysql库中某一张表,或者某几张表的Join关系
  • 分析元数据:比如衍生指标、衍生维度等、指标维度名称,用户在定义数据集的时候,可以根据需要增删

2.2.2 配置面板

配置面板上能够承载用户生产报表的部分功能,用户通过拖动,点选等方式可以设置查询的指标、维度、分析方法等,然后将查询的数据返回给前端,前端进行展示,这块的功能比较多,但都比较简单,业内中做的比较好的有quickBI的分析办,网易有数的分析版,Tableau的面板等,都可以参考,这里放一个:quickBI的地址方便大家查看;

2.3 系统各模块

整体系统模块图如下所示: 报表系统模块图.png

2.3.1 模型管理

模型管理包含三部分信息,用户配置数据源信息、指标维度矩阵(是数据集的支撑)、权限;

  • 数据源信息主要包含数据源类型,连接地址,用户名,密码等信息;
  • 指标维度矩阵的配置包含,关联的数据源,字段是指标还是维度,矩阵是的星型模型/雪花模型是怎样关联的,计算指标,衍生指标,默认格式等信息;
  • 权限包含两个方面,查询该指标维度矩阵要求的权限,某一个字段查询要求的权限,比如支持某个模型的查询要求必须拥有人员相关的组织权限; 该部分的代码主要是增删改查,实现逻辑比较简单;

2.3.2 查询引擎

2.3.2.1 逻辑建模

BI工具的前后端可以约定使用json的形式来描述查询的DSL,描述查询的指标、维度、查询条件、分析方法,简单一个DSL Demo如下:

json
复制代码
{
  "indices": [],
  "dims": [],
  "filters": [],
  "dataSet": "dataSetId",
  "functions": [
    {
      "funcType": "同环比",
      "index": "index1",
      "offset": "7day"
    }
  ]
}

BI查询engine需要将查询将上述描述的DSL转化成查询MySQL、Clickhouse、Hive等数据源是SQL,这个是查询engine解决的最重要问题。
参考OLAP分析数据库查询engine的实现方式,所以BI工具的查询engine也可以通过这种方式实现,将查询的DSL解析成一棵最基本的算子树,然后再基于这个算子树应用优化规则,最终将其转换成满足底层查询数据源满足的语法。(ps:有可能整个查询意图无法完全下推到数据源,那么需要将整体查询拆分成多个部分,分别下推到数据源,然后再在查询engine内链接数据)BI工具查询模型如下:

算子表达式.png 如上图所示整体过程分为三个步骤:

  • 根据查询DSL生成基础的逻辑查询计划(UnResolvedLogicalPlan);
  • 使用分析规则,根据业务规则转化算子树,让其能够表达出完整的业务含义;DSL中的functions字段,大多都需要优化规则来实现;比如同环比,占比规则等,得到逻辑查询计划(LogicalPlan);
  • 将逻辑计划实现成对应的Operator,物理查询计划(PhysicalPlan);物理查询计划节点由Operator构成;

2.3.2.2 业务分析规则

分析方法是BI工具平台中重要的组成部分,用户在配置面板中配置了需要查询一个指标的同环比,查询引擎应该做哪些工作?答案是查询引擎需要增加一个对应分析规则,分析规则会遍历LogicalPlan,将其修改成能表达同环比的含义;
BI工具对应的业务环境复杂,分析方法也会有很大的差异,分离出规则表示分析方法,能够很好的做到动静分离,增加代码可维护性,不能因为某一次改动,影响到整个业务系统的使用;同时也能满足快速的业务变化,增加或者删除分析方法;
如2.3小节图中所示,分析规则输出是一个LogicalPlan,输出也是一个LogicalPlan; 每一个公司针对自己特殊的分析场景,都会有比较特殊的分析规则,这里列举一些比较常见的分析规则;

  • 派生维度规则(指标是否合法,类型是否正确)
  • 强制维度规则(一般为了保证权限、或者查询效率,通常一些查询都要带有必选维度)
  • 派生指标维度解析规则(派生指标需要转化成原子指标,才能下推到数据源查询)
  • 维度打平规则(用于处理级联维度情况下,选择level=1,value="dim1"时,将其转换成对应的级联维度上)
  • 维度下钻规则(用于处理级联下转时,转化成对应的查询语句)
  • 指标维度元数据匹配规则(匹配选择的指标维度到具体的物理表字段和类型)
  • 维度分组规则(生成分组维度)
  • 复合指标计算下推规则(生成符合数据源格式的复合指标计算公式)
  • 同环比规则(生成Join逻辑计划)
  • 维度扩展规则(根据维值Id,Join获取维值的Value)
  • 内存计算规则(无法下推的复合指标,生成对应的计算表达式算子节点MemoryPlan)

每一个规则之间独立,可以保证代码灵活的扩展性,适应快速变化的需求;

2.3.2.3 物理建模

物理建模层上接逻辑建模层产生的结果,可能是一个或者多个LogicalPlan,若是多个的话则是一棵树;并且将对应的LogicalPlan 转化成对应的Opreator,比如:

逻辑转物理计划.png

2.3.2.4 计算建模

Operator 作为物理查询计划的零件,同时也是计算建模零件,参考火山模型,抽象三种Operator:SourceOperator表示是第一个节点,用于读取数据源;MemoryOperator用于读取其他Operator的结果,内存计算;OutputOperator用于读取数据,作为最后一个节点输出;Operator的顶层接口是:

java
复制代码
public abstract DataStruct execute(List<DataStruct> list) throw Exception

根据DAG,执行的过程由叶子节点开始执行,然后父节点,最后根节点输出结果;整个执行过程参考volcano模型设计;

2.3.2.5 数据源连接器

参考Presto设计,数据源连接器的主要作用是为了屏蔽不同数据源连接的异同,通过统一的接口,操作数据源;为上层从数据源读取数据提供一个一致的接口;通过抽象该接口,物理建模层的SourceOperator可通过对应的Connector.source(queryInfo) 获取对应的数据;

java
复制代码
public abstract DataStruct source(QueryInfo queryInfo) throw Exception

2.3.2.6 系统资源管控

内存限制使用.png 为保证服务的稳定性,若查询的数据使用内存过大,可以通过杀死线程,释放内存来保证服务的稳定性; 其中内存的估算部分是基于RamUsageEstimator实现,但是不会估算整个所有结果的大小,这样会产生严重的性能问题,只会随机抽取几条数据,计算每条数据大小的平均值,然后在估算总结果的大小;