大圣魔方——美团点评酒旅BI报表工具平台开发实践

当前的互联网数据仓库系统里,数据中心往往存放了大量Cube化或者半Cube化的数据。如果需要将这些数据的内在关系体现出来,需要写大量的程序和SQL来发现数据之间的内在规律,往往会造成用户做非常多的重复性工作;而且由于没有数据校验的机制,还容易出错,无法直观查看各种数据(没有可视化的UI图表)。这时就急需一款基于Cube的报表工具快速为用户提供报表服务,可以完成多维查询、上卷、下钻等各种功能。为此,美团点评酒旅技术团队开发了大圣魔方。

一款好的BI报表工具,需要考虑并能够解决如下问题:

  • 统一数据源
  • SQL生成
  • 跨数据源数据聚合
  • 自定义计算指标
  • 数据权限
  • 标准化UI组件,自助生成可视化报表

体系架构

arch

图1 大圣魔方体系架构

具体方案

1. 统一数据源

提供多数据源查询服务,需要解决的问题主要是两个:

  1. 以什么样的统一方式从数据源获取数据。
  2. 不是所有的数据源引擎都能提供OLAP服务和数据聚合的能力,我们需要从上层考虑,去实现数据的聚合、上卷、下钻、切割、自定义计算等功能。

data source

图2 大圣魔方多数据源

大圣魔方上对能够通过SQL查询的数据源,例如MySQL和Kylin都通过统一SQL查询来获取数据;对于ES(Elasticsearch)采用ES提供的API来查询;对于普通文本格式的数据采用自定义API从数据源获取数据。

如图2所示,大圣魔方只是从数据源里面获取基础的数据,之后通过实现自己的计算引擎对数据进行聚合、切割等操作,对此,魔方中设置了四个引擎,用于实现不同的功能。

2. SQL生成

对于SQL的生成也存在两个问题:

  1. 不是所有的支持SQL的数据源,都支持标准的SQL,同时,支持标准SQL的数据源也会支持带有自身特征的SQL。
  2. 根据用户选择的条件、维度和指标,动态生成SQL的核心内容。

针对第一个问题,我们对SQL模板进行了定义,当选择不同的数据源时,根据数据源的Dialect选择不同的SQL模板,而这就决定了SQL的组成部分(骨架)。

为了解决第二个问题,我们在SQL模板的基础之上做了内容填充和替换操作,选择具体的维度、指标和筛选项的值,再填充到SQL模板的不同地方,最终就会生成能够被数据源执行的SQL。

在SQL生成的时候也考虑过其它的框架,如Apache Calcite Avatica、Alibaba的Druid,但是最终都放弃了,原因也是基于两个方面:

  1. 这些框架庞大且功能多,适用于我们场景的SQL生成的部分API使用起来过于复杂。
  2. 大都是基于标准的ANSI SQL-92,很难个性化地生成我们所需要的SQL。

最终,我们采用了SQL模板和字符串填充替换操作来完成。为此我们在Java的正则表达式基础之上做了一个功能很多的字符串操作类库。

3. 跨数据源数据聚合

一般情况下,同一个数据源的大部分数据源引擎都能够支持多表的join操作,但是也存在不支持的,例如老版本的Kylin就不支持多Cube的join操作,还有一个更重要的问题是数据源引擎无法解决跨数据源的数据聚合问题,必须要自己实现数据的聚合操作,一般的情况下需要自己去实现inner join、left outer join和full outer join的逻辑。

大圣魔方实现了inner join和left outer join两个逻辑,因为full outer join的需求场景不是很多,所以没有实现。下面是大圣魔方的实现代码:

inner join核心代码

private void join(List<Map<String,String>>[] contents,List<Project> sharedList,final int n,int[] rowsStatus,LinkedList<MatchRow> result){if(this.cubeJoin==1){throw new java.lang.IllegalArgumentException("left join call leftJoin method,not call join method");}if(n<contents.length){List<Map<String,String>> list = contents[n];for(int k=0;k<list.size();k++) {boolean equal = true;if(n!=0) {Map<String, String> prev = contents[n - 1].get(rowsStatus[n - 1]);Map<String, String> cur = list.get(k);for (Project proj : sharedList) {String key = proj.fieldName.toUpperCase();if (key.matches("^\\d+$") || key.equals("*")) {key = "_";}key = proj.isCompanion() ? key + proj.getFactId() : key;String prevValue = prev.get(key);String curValue = cur.get(key);if (prevValue == curValue) {continue;}if (prevValue == null || curValue == null || !prevValue.equals(curValue)) {equal = false;break;}}}if (equal) {rowsStatus[n] = k;if(n==contents.length-1){//last dataset matchMatchRow mr = new MatchRow();List<MatchRow.DatasetRow> tmp = new ArrayList<>();for(int i=0;i<rowsStatus.length;i++){MatchRow.DatasetRow dr = new MatchRow.DatasetRow();dr.setDatasetIndex(i);dr.setRowIndex(rowsStatus[i]);tmp.add(dr);}mr.addMatchRow(tmp);result.add(mr);}else{join(contents,sharedList,n+1,rowsStatus,result);}}}}}

上述代码就是通过回溯算法实现inner join的核心逻辑,具体解析如下:

  • contents参数表示每个数据源里面的结果集。
  • sharedList表示关联的字段。
  • n和rowsStatus是回溯算法记录状态用的。
  • result里面包含的是符合join条件的记录。
  • MatchRow里面记录的是一个数据源里面的某一行与其余的数据源里面的那一行是相等的,记录的是下标号。

只有当sharedList里面的每个字段都相等的时候,两条记录才满足inner join的条件。这个算法是一个通用算法,因为是通过回溯算法实现的,所以要join的数据源理论上可以有无限个。

left outer join核心代码


private boolean leftJoin(List<Map<String,String>>[] contents,List<Project> sharedList,final int n,int[] rowsStatus,LinkedList<MatchRow> result){boolean leftJoinMatch = false;if(n<contents.length){List<Map<String,String>> list = contents[n];for(int k=0;k<list.size();k++) {boolean equal = true;if(n!=0) {//in left join,compare with the first dataset.Map<String, String> prev = contents[0].get(rowsStatus[0]);Map<String, String> cur = list.get(k);for (Project proj : sharedList) {String key = proj.fieldName.toUpperCase();if (key.matches("^\\d+$") || key.equals("*")) {key = "_";}key = proj.isCompanion() ? key + proj.getFactId() : key;String prevValue = prev.get(key);String curValue = cur.get(key);if (prevValue == curValue) {continue;}if (prevValue == null || curValue == null || !prevValue.equals(curValue)) {equal = false;break;}}}if (equal) {leftJoinMatch = true;rowsStatus[n] = k;if(n==contents.length-1){//last dataset matchMatchRow mr = new MatchRow();List<MatchRow.DatasetRow> tmp = new ArrayList<>();for(int i=0;i<rowsStatus.length;i++){MatchRow.DatasetRow dr = new MatchRow.DatasetRow();dr.setDatasetIndex(i);dr.setRowIndex(rowsStatus[i]);tmp.add(dr);}mr.addMatchRow(tmp);result.add(mr);}else{//if next dataset is not match,use the next's next...for(int loopFlag=n+1;loopFlag<rowsStatus.length;loopFlag++){boolean match = leftJoin(contents,sharedList,loopFlag,rowsStatus,result);if(match){break;}rowsStatus[loopFlag]=-1;if(loopFlag==contents.length-1){MatchRow mr = new MatchRow();List<MatchRow.DatasetRow> tmp = new ArrayList<>();for(int i=0;i<rowsStatus.length;i++){MatchRow.DatasetRow dr = new MatchRow.DatasetRow();dr.setDatasetIndex(i);dr.setRowIndex(rowsStatus[i]);tmp.add(dr);}mr.addMatchRow(tmp);result.add(mr);}}}}}}return leftJoinMatch;}

上面的代码是left outer join的实现逻辑,同样也是用的回溯算法,它与inner join有2个不同之处:

  1. left outer join的数据源匹配逻辑是当第一个数据源与第二个数据源没有匹配的时候,会继续与第三个数据源进行匹配操作,原因是数据源的顺序导致了不匹配,继续往下匹配就可以避免这个问题。
  2. 行与行做相等操作的时候,右边没有匹配行的时候,左边的行继续保留,这个是left outer join的逻辑决定的。

4. 自定义计算指标

使用自定义计算的原因,主要是基于下面的两个方面:

  1. 数据源引擎不支持数据的混合运算或有特殊逻辑的数据处理。
  2. 结果数据跨数据源。

对此,我们对大圣魔方做了如下操作:

  1. 通过Java里面的ScriptEngine进行封装来实现数据列的混合运算,不需要自己再去写编译程序解析。对于特殊的数据处理,例如同环比这样的特殊指标,需要单独定义接口,让实现类继承改特定接口,实现类是一个特殊的指标,它需要进行多次数据查询,将最终的结果通过ScriptEngine进行运算。
  2. 第二个问题是在上文中“跨数据源数据聚合”的基础上实现的,数据聚合后通过ScriptEngine做最后的处理。

5. 数据权限的问题

只要是有数据展示,数据权限问题就无法避免,权限主要是分为报表的可查看权限和维度、指标权限。权限遇到的最主要的问题是构成权限矩阵的数据量太大,参与者有用户和组织,权限的实体有维度和指标,这样大的数据维护起来的成本很高;其次是权限数据配置会占用很多的人力。

对此,我们做了如下操作:

  1. 使用UPM控制报表的可查看权限。公司推荐使用UPM来控制权限,不过UPM也具有一定的局限性,即只能够判断用户或者组织是否满足某个权限,而不能满足获取部分权限数据的需求,例如某个用户对某个权限只拥有一部分权限,那他就无法提供具体数据。但是UPM可以提供是否的权限,所以报表的可查看权限可以使用UPM来控制,这样可以节约大量的工作。
  2. 使用默认任何人都有权限的机制。通过使用默认有权限的这个机制可以大大减少权限数据。需要鉴权的那些维度和指标采用默认无权限的机制,这样两种方案结合,可以最大限度地减少权限数据。
  3. 通过走审批流机制自助申请。通过审批流机制可以让用户走自助申请,大大节约权限数据的维护人力成本。

6. 标准化UI组件,自助生成可视化报表

报表上展示数据需要有各种各样的图表,没法为用户只做一个统一的报表,这个时候需要用户能够创建自己想要的报表,这时需要提供一个标准的组件库、布局库和一些常用的模板。用户选择好想要的模板,然后选择布局对报表页面进行布局,接着在每个布局里面填充不同的组件,这样就可以构建一张报表了,也就是我们常说的所见即所得的方式。

大圣魔方就是采用上述的机制提供了一套可视化报表编辑工具。使用它可以快速地创建一个报表,管理人员只需要维护对应的组件、布局和模板就行了。

上述几点就是大圣魔方的一个总纲,其中大部分功能已经实现了,还有一小部分处于开发之中(标准化UI组件、自助生成可视化报表)。目前大圣魔方已经上线将近一年了,支持了内部众多业务,后续我们还会在UI易用性、星型模型、配置简化、元数据同步等方面做一些提高。

最后插播一个招聘广告,有对BI工具开发感兴趣的可以发邮件给 fuyishan@meituan.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/478542.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于知识图谱的智能问答方案

基于知识图谱的智能问答方案&#xff1a;https://cloud.tencent.com/developer/article/1661504 基于知识图谱的智能问答方案2020-07-142020-07-14 15:57:50阅读 9950三个角度理解知识图谱2012年谷歌首次提出“知识图谱”这个词&#xff0c;由此知识图谱在工业界也出现得越来越…

论文浅尝 - ACL2020 | 用于实体对齐的邻居匹配网络

笔记整理 | 谭亦鸣&#xff0c;东南大学博士来源&#xff1a;ACL 20链接&#xff1a;https://www.aclweb.org/anthology/2020.acl-main.578.pdf1.介绍图谱之间的异构差异是建立实体对齐的一个主要挑战&#xff0c;本文提出了Neighborhood Match Network (NMN)&#xff0c;用于处…

LeetCode 117. 填充每个节点的下一个右侧节点指针 II(递归循环)

文章目录1. 题目2. 解题2.1 递归2.2 queue循环2.3 利用next循环1. 题目 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL。 初始状态下&#xff0c;所有 next 指针都被设置为 NULL。 类似…

美团点评境外度假团队前端项目开发实践总结

随着前端项目数量和规模越来越大&#xff0c;参与的人员也越来越多&#xff0c;如何在前端项目开发过程中保证优质的开发者体验和项目的可维护性&#xff0c;同时确保极致的用户体验将会是一个非常大的挑战。 为了应对这个挑战&#xff0c;美团点评境外度假前端研发团队自2016年…

线性代数不深入,机器学习两行泪!

我经常听到有人说&#xff0c;机器学习很难&#xff0c;到底怎么学更高效&#xff1f;其实&#xff0c;我想说&#xff0c;机器学习本身没有多大难度&#xff0c;因为经过多年的积累后&#xff0c;很多规则已经成型了。对于我们来说真正难的&#xff0c;是机器学习背后的算法所…

反爬虫机制和破解方法汇总

https://cloud.tencent.com/developer/article/1032918 什么是爬虫和反爬虫&#xff1f;爬虫&#xff1a;使用任何技术手段&#xff0c;批量获取网站信息的一种方式。反爬虫&#xff1a;使用任何技术手段&#xff0c;阻止别人批量获取自己网站信息的一种方式。常见的反爬虫机制…

论文小综 | 知识图谱表示学习中的零样本实体研究

转载公众号 | 浙大KG 本文作者| 耿玉霞&#xff0c;浙江大学在读博士&#xff0c;主要研究方向为知识图谱、零样本学习及可解释性前言随着知识图谱表示学习算法的蓬勃发展&#xff0c;在各个领域中都得到了广泛的应用&#xff0c;如推荐系统、知识问答等&#xff0c;以及知识图…

LeetCode 297. 二叉树的序列化与反序列化(前序遍历层序遍历)

文章目录1. 题目2. 解题2.1 前序遍历2.2 层序遍历1. 题目 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传输到另一个计算机环境&#xff0c;采取相反方式重构得到原数据…

互联网企业安全之端口监控

外网端口监控系统是整个安全体系中非常重要的一环&#xff0c;它就像眼睛一样&#xff0c;时刻监控外网端口开放情况&#xff0c;并且在发现高危端口时能够及时提醒安全、运维人员做出相应处理。 对安全人员来说&#xff0c;互联网公司在快速发展壮大的过程中&#xff0c;外网边…

知乎热榜:程序员达到什么水平能拿到20k月薪

昨天在知乎上刷到一个热门问题:程序员需要达到什么水平才能顺利拿到 20k 无压力&#xff1f;其中一个最热门的回答是&#xff1a;“其实&#xff0c;无论你是前端还是后端、想进大厂还是拿高薪&#xff0c;算法都一定很重要。”为什么&#xff0c;算法会如此重要&#xff1f;不…

研究综述 | 知识图谱划分算法研究综述

作者 | 王鑫&#xff0c;天津大学智能与计算学部来源 | 计算机学报知识图谱划分是大规模知识图谱分布式处理的首要工作&#xff0c;是知识图谱的分布式存储、查询、推理和挖掘的基础支撑。从知识图谱和图划分的定义出发&#xff0c;系统性地介绍当前可用于知识图谱数据划分的各…

深度学习中不得不学的Graph Embedding方法

原文链接&#xff1a;https://zhuanlan.zhihu.com/p/64200072 深度学习中不得不学的Graph Embedding方法王喆​数据挖掘等 3 个话题下的优秀答主​关注他1,290 人赞同了该文章这里是「王喆的机器学习笔记」的第十四篇文章&#xff0c;之前已经有无数同学让我介绍一下Graph Embe…

写给新手炼丹师:2021版调参上分手册

文 | 山竹小果在日常调参的摸爬滚打中&#xff0c;参考了不少他人的调参经验&#xff0c;也积累了自己的一些有效调参方法&#xff0c;慢慢总结整理如下。希望对新晋算法工程师有所助力呀&#xff5e;寻找合适的学习率(learning rate)学习率是一个非常非常重要的超参数&#xf…

函数式编程在Redux/React中的应用

本文简述了软件复杂度问题及应对策略&#xff1a;抽象和组合&#xff1b;展示了抽象和组合在函数式编程中的应用&#xff1b;并展示了Redux/React在解决前端状态管理的复杂度方面对上述理论的实践。这其中包括了一段有趣的Redux推导。 软件复杂度 软件的首要技术使命是管理复杂…

论文浅尝 - EMNLP2020 | ConceptBert:视觉问题回答的概念感知表示

笔记整理 | 陈卓&#xff0c;浙江大学计算机科学与技术系&#xff0c;博士研究生研究方向 | 知识图谱/图神经网络/多模态论文链接&#xff1a;https://www.aclweb.org/anthology/2020.findings-emnlp.44.pdf代码&#xff1a;https://github.com/ZiaMaryam/ConceptBERT发表会议&…

LeetCode 215. 数组中的第K个最大元素(快速排序)

1. 题目 在未排序的数组中找到第 k 个最大的元素。请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 示例 1: 输入: [3,2,1,5,6,4] 和 k 2 输出: 5示例 2: 输入: [3,2,3,1,2,4,5,5,6] 和 k 4 输出: 4说明: 你可以假设 k…

论文浅尝 - EMNLP2020 | 通过词重排序跨语言解析

笔记整理 | 吴林娟&#xff0c;天津大学硕士来源&#xff1a;EMNLP2020链接&#xff1a;https://www.aclweb.org/anthology/2020.findings-emnlp.265.pdf动机依赖解析研究快速发展&#xff0c;然而依赖解析的性能在很大程度上依赖于语料库的大小。获取足够的训练数据成本大且困…

Knowledge Review:超越知识蒸馏,Student上分新玩法!

文 | 陀飞轮知乎今天介绍一篇我司的文章Distilling Knowledge via Knowledge Review(缩写为KR)&#xff0c;提出知识蒸馏的新解法。之前在知乎回答过一个知识蒸馏的问题&#xff0c;为何感觉“知识蒸馏”这几年没有什么成果&#xff1f;https://www.zhihu.com/question/3098084…

美团App 插件化实践

背景 在Android开发行业里&#xff0c;插件化已经不是一门新鲜的技术了&#xff0c;在稍大的平台型App上早已是标配。进入2017年&#xff0c;Atlas、Replugin、VirtualAPK相继开源&#xff0c;标志着插件化技术进入了成熟阶段。但纵观各大插件框架&#xff0c;都是基于自身App的…

评测征集 | 2021全国知识图谱与语义计算大会

CCKS 2021将组织知识图谱相关评测竞赛&#xff0c;旨在为研究者们提供一个测试技术、算法、及系统的平台。与CCKS 2020 一样&#xff0c;CCKS 2021 的评测任务仍然采用 Biendata 在线平台发布和评测。CCKS 2020评测竞赛环节共设立8个任务&#xff0c;吸引了2300多支参赛队伍。大…