写点什么

Apache Kylin 权威指南(九):Top_N 度量优化

2020 年 4 月 21 日

Apache Kylin权威指南(九):Top_N 度量优化

编者按:本文节选自华章科技大数据技术丛书 《Apache Kylin 权威指南(第 2 版)》一书中的部分章节。


Top_N 度量优化

在生活中我们总能看到“世界 500 强公司” “销量最好的十款汽车”等标题的新闻报道,Top_N 分析是数据分析场景中常见的需求。在大数据时代,由于明细数据集越来越大,这种需求越来越明显。在没有预计算的情况下,得到一个分布式大数据集的 Top_N 结果需要很长时间,导致点对点查询的效率很低。


Kylin v1.5 以后的版本中引入了 Top_N 度量,意在进行 Cube 构建的时候预计算好需要的 Top_N,在查询阶段就可以迅速地获取并返回 Top_N 记录。这样,查询性能就远远高于没有 Top_N 预计算结果的 Cube,方便分析师对这类数据进行查询。


注意 这里的 Top_N 度量是一个近似的实现,如你想要了解其近似度,需要在本节之后的内容中更多地了解 Top_N 背后的算法和数据分布结构。


让我们用 Kylin 中通过 sample.sh 生成的项目“learn_kylin” 对 Top_N 进行说明。我们将重点使用其中的事实表“kylin_sales”。


这张样例表 “default.kylin_sales” 模拟了在线集市的交易数据,内含多个维度和度量列。这里仅用其中的四列即可:“PART_DT” “LSTG_SITE_ID” “SELLER_ID”和“PRICE”。表 3-1 所示为这些列的内容和基数简介,显而易见 “SELLER_ID” 是一个高基列。


表 3-1 样例表 “default.kylin_sales”的情况


列名描述基数
PART_DT交易日期730:两年
LSTG_SITE_ID交易站点ID,如美国站用00代表50
SELLER_ID卖家ID大约100万
PRICE销售额


假设电商公司需要查询特定时段内,特定站点交易额最高的 100 位卖家。查询语句如下:


SELECT SELLER_ID, SUM(PRICE) FROM KYLIN_SALES WHERE   PART_DT >= date'2012-02-18' AND PART_DT < date'2013-03-18'      AND LSTG_SITE_ID in (0)   group by SELLER_ID   order by SUM(PRICE) DESC limit 100
复制代码


方法 1:在不设置 Top_N 度量的情况下,为了支持这个查询,在创建 Cube 时设计如下:定义“PART_DT”“LSTG_SITE_ID”“SELLER_ID”作为维度,同时定义 “SUM(PRICE) ”作为度量。Cube 构建完成之后,Base Cuboid 如表 3-2 所示。


表 3-2 Cube 的 Base Cuboid 结构


Base Cuboid的RowKeySUM(PRICE)
20120218_00_seller0000001291.58
20120218_00_seller0000002365.18
20120218_00_seller0000003135.29
20120218_00_seller1000000272.31
20120218_01_seller0000001172.52


假设这些维度是彼此独立的,则 Base Cuboid 中行数为各维度基数的乘积:730 * 50 * 1 million = 36.5 billion = 365 亿。其他包含“SELLER_ID”字段的 Cuboid 也至少有百万行。由此可知,由“SELLER_ID”作为维度会使得 Cube 的膨胀率很高,如果维度更多或基数更高,则情况更糟。但真正的挑战不止如此。


我们可能还会发现上面那个 SQL 查询并不能正常执行,或者需要花费特别长的时间,原因是这个查询拥有太大的在线计算量。假设你想查 30 天内 site_00 销售额排前 100 名的卖家,则查询引擎会从存储引擎中读取约 3000 万行记录,然后按销售额进行在线排序计算(排序无法用到预计算结果),最终返回排前 100 名的卖家。由于其中的关键步骤没有进行预计算,因此虽然最终结果只有 100 行,但计算耗时非常长,且内存和其中的控制器都在查询时被严重消耗了。


反思以上过程,业务关注的只是销售额最大的那些卖家,而我们存储了所有的(100 万)卖家,且在存储时是根据卖家 ID 而不是业务需要的销售额进行排序的,因此在线计算量非常大,因而此处有很大的优化空间。


方法 2:为了得到同一查询结果。如果在创建 Cube 时,对需要的 Top_N 进行了预计算,则查询会更加高效。如果在创建 Cube 时设计如下:不定义“SELLER_ID”为维度,仅定义“PART_DT”“LSTG_SITE_ID”为维度,同时定义一个 Top_N 度量,如图 1 所示。



图 1 TOP\_N 度量的设置示例


注意 “PRICE”定义在“ORDER|SUM by Column”,“SELLER_ID”定义在“Group by Column”。


新 Cube 的 Base Cuboid 如表 3-3 所示,Top_N 度量的单元格中存储了按 seller_id 进行聚合且按 sum(price) 倒序排列的 seller_id 和 sum(price)的组合。


表 3-3 新 Cube 的 Base Cuboid 结构


base cuboid的RowKeyTop_N Measure
20120218_00seller0010091:1092.21, seller0005002:1090.35,
…,
seller0001789:891.37
20120218_01seller0003012:xx.xx
seller0004001:xx.xx,
…,
seller000699: xx.xx
20120218_02
20120218_50
20120219_01seller0010091:1012.74, seller0005002:1032.63
…,
seller0001981:883.57


现在 Base Cuboid 中只有 730 * 50 = 36500 行。在度量的单元格中,预计算的 Top_N 结果以倒序的方式存储在一个容器中,而序列尾端的记录已经被过滤掉。


现在,对于上面那个 Top_100 seller 的查询语句,只需要从内存中读取 30 行,Kylin 将会从 Top_N Measure 的容器中抽出“SELLER_ID”和“SUM(PRICE)”,然后将其返回客户端(并且是已经完成排序的)。现在查询结果就能以亚秒级返回了。


一般来说,Kylin 在 Top_N 的单元格中会存储 100 倍的 Top_N 定义的返回类型的记录数,如对于 Top100,就存储 10000 条 seller00xxxx:xx.xx 记录。这样一来,对于 Base Cuboid 的 Top_N 查询总是精确的,不精确的情况会出现在对于其他 Cuboid 的查询上。举例来说,对于 Cuboid[PART_DT],Kylin 会将所有日期相同而站点不同的 TOP_N 单元格进行合并,这个合并后的结果会是近似的,尤其是在各个站点的前几名卖家相差较多的情况下。比如,如果 site_00 中排第 100 名的卖家在其他站点中都排在第 10000 名以后,那么它的 Top_N 记录在其他站点都会被舍去,Kylin 在合并 TOP_N Measure 时发现其他站点里没有这个卖家的值,于是会赋予这个卖家在其他站点中的 sum(price)一个估计值,这个估计值既可能比实际值高,也可能比实际值低,最后它在 Cuboid[PART_DT]中的 Top_N 单元格中存储的是一个近似值。


图书简介https://item.jd.com/12566389.html



相关阅读


Apache Kylin权威指南(一):背景历史和使命


Apache Kylin权威指南(二):工作原理


Apache Kylin权威指南(三):技术架构


Apache Kylin权威指南(四):核心概念


Apache Kylin权威指南(五):Getting Started


Apache Kylin权威指南(六):Cuboid剪枝优化


Apache Kylin权威指南(七):剪枝优化工具


Apache Kylin权威指南(八):Rowkey 优化


2020 年 4 月 21 日 10:00513

评论

发布
暂无评论
发现更多内容

食堂卡就餐卡系统

叶鹏

被我玩坏的git:除了之前的工作、当网盘用,还能这么玩

小Q

Java git 程序员 架构 开发

ECMAScript 6新特性简介

程序那些事

nodejs ES6 ECMAScript 6

小白理财先转变思维理念

boshi

理财 收入 财富自由

简述JVM垃圾回收

叶鹏

架构师训练营第八周作业

叶鹏

一文学懂递归和动态规划!

码农田小齐

算法 数据结构和算法

18 张图,一文了解 8 种常见的数据结构

沉默王二

Java 数据结构

实战中学习浏览器工作原理 — 排版与渲染

三钻

CSS 前端 浏览器

年度开源盛会 ApacheCon 来临,Apache Pulsar 专场大咖齐聚

Apache Pulsar

开源 云原生 Apache Pulsar 消息中间件

架构师第1课作业及学习总结

小诗

Spring 5 中文解析数据存储篇-@Transactional使用

青年IT男

spring

关于Java调用类的main方法

谷鱼

Java 包位置

两天,我把分布式事务搞完了

yes的练级攻略

分布式事务 seata

Python 中 \x00 和空字符串的区别,以及在 Django 中的坑

AlwaysBeta

Python django 编程

架构师训练营12周作业

叶鹏

常用设计模式

叶鹏

一个草根的日常杂碎(9月21日)

刘新吾

生活 现实纪录 随笔

【性能优化】面试官:Java中的对象都是在堆上分配的吗?

冰河

面试 性能优化 JVM 性能调优 逃逸分析

架构师训练营第四周作业

叶鹏

前端如何优雅处理类数组对象?

pingan8787

Java web前端

oeasy 教您玩转linux 010304 图形界面 xfce

o

高难度对话读书笔记—情绪篇

wo是一棵草

微服务的框架(Dubbo)架构

叶鹏

Spring 5 中文解析数据存储篇-编程式事物管理

青年IT男

Spring5

简述 CAP 原理

叶鹏

架构师训练营第7周作业

叶鹏

Springboot 定时任务

hepingfly

定时任务 springboot 注解

从零开始搭建完整的电影全栈系统(五)——WEB网站、Api以及爬虫的部署

刘强西

爬虫 网站搭建 部署与维护

用户密码验证函数

叶鹏

anyRTC云端录制功能上线

anyRTC开发者

WebRTC 语音 直播 RTC 安卓

微服务架构下如何保证事务的一致性

微服务架构下如何保证事务的一致性

Apache Kylin权威指南(九):Top_N 度量优化-InfoQ