如何实现海量数据下有序漏斗秒查

近期易观公司举办了一个OLAP大赛,我们队伍非常荣幸地获得了第一名,成为本次比赛最大黑马。此篇文章主要分享一下我们是如何解决有序漏斗秒查问题的

比赛地址:2017易观OLAP算法大赛

参赛情况: https://www.analysys.cn/media/detail/20018458/

1. 题目分析

在以上示例场景下,我们在易观提供的6亿测试数据集上,在4台UCloud云主机(16core,16G ram)机器下从24s优化到了0.5s。而在正式比赛的26亿数据集上,使用相同硬件环境,耗时1.6s。

2. 解题分析:

题目描述的有序漏斗问题可以归结为 带滑动时间窗口的最左子序列问题, 比如我们需要寻找,2017年7月份中,在3小时的时间窗口下, [A,B,C,D] 漏斗路径下的转化情况, 单个用户只能有 NULL , [a], [A,B], [A,B,C], [A,B,C,D] 五种转化结果,对应的漏斗深度我们称之为level,在[A,B,C,D]漏斗路径下,level的取值可以有[0,1,2,3,4] 四个值,题目的要求即算出所有用户的满足条件下最大level汇总结果。

理解问题之后,我们梳理了一下流程图:

19232231_gzNR.jpg

我们将问题解决分为5个步骤:

  1. filter阶段 :根据时间区间和事件属性对数据进行过滤
  2. group阶段: 根据用户Id进行group汇总
  3. sort阶段: 按照时间进行排序
  4. algorithm aggregate 阶段: 带时间窗口的子序列搜索
  5. 合并结果

3. 数据库选型:

根据以上分析,需要filter,group,sort,aggregate等操作,数据库是必备的核心,而在OLAP领域,开源的数据库选型有很多,比如:mysql, druid, kylin hdfs + (hive,spark,presto),imapla, kudu etc。

但在这个场景下,结合以往对其他数据的深入研究分析对比经验,我们几乎毫不犹豫就选择了 ClickHouse (纵然它不支持udaf),,我们相信ClickHouse是目前cpu领域最快的olap开源数据库,它最突出的优点就是快,如果你是第一次用,相信ClickHouse会让你感到非常惊艳。

231658_KtWm_568818.png

ClickHouse 由俄罗斯Yandex开发,09年原型,12年生产可用,16年开源,目前最大的线上部署实例是 Yandex.Metrica: 472个节点,每秒处理2T数据,实时在线分析。ClickHouse 在OLAP上的查询性能非常彪悍,平均查询性能几乎是vertica的三倍。

19232231_17D5.jpg

ClickHouse不仅速度快,它系统架构灵活,性能优越,代码优雅, 非常适合大数据下需要极致性能的应用场景。ClickHouse目前暂不支持UDAF,但没关系,我们可以通过修改源代码并重新编译来实现自定义AggregateFunction。

231724_AhRs_568818.png

以上就是针对漏斗场景的代码修改情况,可以看出我们只用了不到300行代码就为ClickHouse加入了漏斗计算(aggregate function path)的功能。

对比官方的presto + hdfs 方案实现的24s速度,使用ClickHouse之后,我们在测试环境下跑的速度达到了8s。

下面开始我们的正式优化过程

4. 按照用户ID分区:

231746_aSy5_568818.png

我们注意到,漏斗的计算中,每个用户都是相互独立的,所以我们可以将数据按照uid来分区,这样就将数据分散到了四台机器上,我们可以分别向每个数据库节点发送请求,然后将数据进行汇总,得到最终结果。

通过这次优化,我们在测试环境下跑的速度达到了1.6s。

5. 以(uid,timestamp)作为primary key

19232231_NtZc.jpg

ClickHouse中primary key代表了数据的组织排序方式

左边的以(timestamp,uid)为主键,时间是有序的,方便做时间精准过滤,但uid压缩率低;右边的以(uid,timestamp) 为主键,uid压缩率高,方便做uid group, 但对时间过滤支持不够好。

通过测试对比,我们发现以 (uid,timestamp) 作为主键性能略快,查询时间达到了 1.4s。

6. 分组预排序

231822_3TBO_568818.png

当我们以 (uid,timestamp) 作为primary key后, 分组内的数据其实已经有序了, 我们可以去掉代码中的sort方法,来提高性能,经过这个优化,查询时间达到了 0.9s。

7. 带时间窗口的子序列搜索优化

19232231_QwlZ.jpg

这里主要是用了一些 剪枝的策略,当我们从左往右去搜索 a,b,c,d 漏斗的时候,我们需要找到最大的深度,必须一直去搜索以a开头的子序列;但我们从右往左搜索时, 我们只要考虑比当前结尾更大的子串即可, 比如我们找到了 a,b,c, 后面我们只需要考虑以d结尾的串,这样减小了搜索的复杂度,查询时间达到了0.8s。

8. 数据结构优化

事件ID到数组下标Index的映射,我们直接遍历了数组搜索,而不使用std::unordered_map, 因为在events数据量不大的情况下, 数组搜索O(1)比O(n)慢。

使用纯C++数组存储事件序列,不使用std::vector,去掉了vector的开销,灵活控制内存分配。

经此优化,查询时间达到了0.6s。

9. 部分压缩

数据库通常会对字段进行压缩,这样做节省了硬盘空间,但却浪费了cpu计算,为了提供性能,我们对字段进行了部分压缩,经此优化,查询时间达到了最终的0.5s。

  • uid => 压缩
  • timestamp => 不压缩
  • event_id => 不压缩
  • event_name => 压缩
  • event_tag => 压缩
  • date => 不压缩

总结:

我们已经将代码和PPT开源:
https://github.com/analysys/olap


0.5s当然不会是极限,如今GPU数据库大道横行,技术变革也愈演愈烈, 前路漫长,预测未来最好的方法就是自己创造未来。

Refer

[1] 如何实现海量数据下有序漏斗秒查

https://zhuanlan.zhihu.com/p/30823204

[2] analysys/olap

https://github.com/analysys/olap

[3] 易观olap大赛 

http://ds.analysys.cn/OLAP.html

[4] 易观OLAP算法大赛结果揭晓,开源组黑马放大招!

https://www.analysys.cn/media/detail/20018458/

转载于:https://my.oschina.net/leejun2005/blog/118627

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值