Kyligence Copilot - AI 数智助理,以 AI 变革企业经营与管理! 立即了解更多

Kylin 在 58 集团的实践和应用

何良均
58 大数据平台资深工程师
2019年 5月 27日

在近期的 Apache Kylin Meetup 北京站上,我们邀请到 58 大数据平台资深工程师何良均对 Kylin 在 58 集团的实践和应用进行了分享。

何良均

何良均来自 58 同城数据平台部,目前负责 HBase 和 Kylin 的优化、维护和开发工作。当天,他为大家分享了 Kylin 平台的优化、新版本升级以及应用案例。

01

平台优化

目前在 58,用户可以通过两种方式来接入 Kylin 平台。一种是通过我们数据产品部开发的“魔方”接入,“魔方”是一个多维分析的 BI 平台。另一种是直接通过域名连到 Kylin 平台,来进行 Cube 查询和构建。

Kylin 依赖的 Yarn 和 HBase 集群,在 58 都是多租户集群,所以我们对 Kylin 做了一些多租户的优化,后面会讲到。另外,针对 Cube 的调度,我们还开发了一套调度系统,还有其他的一系列的管理工具。

先说一下我们的 Yarn 集群、HDFS 集群和 HBase 集群的现状。在 Yarn 方面,不同用户提交的作业会跑在对应的队列上。在 HDFS 方面,我们对用户权限控制比较严,用户目录权限设置为 500,其他用户是没法访问的。在 HBase 方面,我们将 Hadoop 账号和 HBase 账号进行了打通,同时使用 HBase 的 Namespace 机制来做权限隔离,即不同的用户使用不同的 Namespace 来隔离表。由于以上多方面的现状,我们在引入 Kylin 提供给用户使用时,不得不面对 Kylin 需要支持多租户的问题。

以下是我们在多租户方面做的优化

我们说一下 Kylin 的多租户现状,它其实是支持多用户登录的。

针对用户和角色,Cube 有不同的访问权限。但是它在使用底层集群资源的时候,比如说 Yarn、HBase 或者 HDFS 的时候,用的是 Kylin 启动的账号。而使用我们 Kylin 的用户,来自于公司各个不同的部门,他们对应的 Yarn 有不同的队列,HDFS 有不同的目录,HBase 对应了不同的 Namespace。我们需要做到对不同的登录用户,在使用 Yarn、HDFS、HBase 的时候,能够用对应的登录用户去访问不同的集群资源。

下图是我们做多租户优化的目标:

根据前面的目标,我们做了一些开发工作,以下是我们实现的步骤。由于我们的 Hadoop 集群使用的是自研的一套权限认证,简单但是很适用,一定程度上减轻了 Kylin 多租户功能开发工作。

修改 Kylin 的 security profile 为 testing,在 testing 模式下,针对用户管理就可以实现自定义功能了。我们自定义了一个用户配置文件,叫做 kylin-user.properties 文件。我们重新实现了用户的详细接口,会动态、定时地去刷新文件到内存里去,这样的话在内存里边就有用户信息了,用户在登录的时候,就可以拿到登录信息。

当 Kylin 的构建任务使用 Yarn 时,Kylin 支持在 Cube 这一级去配置队列参数。如果说 Cube 上没有配置,就使用 project 上的队列配置。如果 project 上没有配置,就会按登录用户,按默认算法生成队列。

HDFS 这一块也是一样的,当需要面临 HDFS 上数据的访问,我们会用登录用户生成 Hadoop UGI,然后在 Hadoop UGI 上执行 doAs(),这样的话就会把登录用户的信息传递给 HDFS。我们用到的 Hive 模式就是客户端模式,会在本地提交一个 Hive 脚本,我们会根据登录用户设置环境变量 HADOOP_PROXY_USER 来实现多租户功能。

对于 HBase,Kylin 会用来进行数据存储和查询。就像之前说的,我们会创建不同的 Namespace 来隔离不同用户的表,同时 Namespace 是和 Hadoop 账户一一对应的。在创建 Kylin 表时,我们使用登录用户作为表的 Namespace。查询的时候,用登录用户生成 Hadoop UGI 去构造 HBase 的 connection 去查询,这样就简单实现了 Kylin 使用 HBase 的多租户。

以上就是我们多租户的优化。

下面讲一下我们在 Kylin 上做的一些优化

第一个是维度字典上传优化。现象是用户在使用 Kylin 过程中,出现了部分 Cube 的 Build 任务执行延迟较大,甚至无法启动的情况,而且根据统计对比发现整个集群任务构建时间逐渐增加。

原因是 Cube 构建过程中,有多个步骤需要运行 MR 作业,同时需要将包括维度字典文件(维度编码设置为了字典)以及其他的元信息文件作为分布式缓存上传 HDFS,并下载到计算节点本地。随着时间的推移,字典文件会越来越多(Segment增多),导致总的上传时间越来越长。当文件总大小太大时,出现分布式缓存文件上传超时,最终任务无法启动。

以下是 Cube 构建中,单个 MR 作业进行分布式缓存文件上传下载流程:

Cube 构建 MR 作业分布式缓存上传下载文件默认包括元信息文件以及字典文件:元信息包括 Cube 信息、Segment 信息、Fact 表信息、Model 信息以及配置信息等;字典文件就是维度字典文件,默认会包括所有 Segment 的维度字典文件,随着时间推移,Segment 会越来越多,维度字典文件的数据量会越来越大。

以下是 Cube 构建的整体流程图:

整个构建过程包括多个 MR 作业,其中分布式缓存上传和下载元信息文件和字典文件的 MR 作业包括①、②、③、④、⑤、⑥,分别对应:

①、对维度字段进行去重的 MR 作业;

②、计算 Base Cuboid 的 MR 作业;

③-⑤、计算 N-1 至 0 维 Cuboid 的 MR 作业,可能有很多个 MR 作业,由维度个数决定;

⑥、根据前面 ② 至 ⑤ 生成的 Sequence File 最终聚合生成 HFile 的 MR 任务。

默认情况下以上所有 MR 作业都会通过分布式缓存上传和下载元信息文件和 Cube 对应所有 Segment 的维度字典文件。通过仔细分析发现,我们可以进行以下两种优化:

第一种:有的步骤并不需要 Segment 维度字典文件,比如①、③、④、⑤、⑥。

第二种:有的步骤只需要部分维度字典,比如②的构建 Base Cubeid 的 MR 作业,只需要当前构建 Segment 的维度字典。在 Cube 的 Segment 合并时,也只需要参与合并的 Segment 的维度字典文件,可以类似进行优化。

我们对上传的 Segment 维度字典文件的流程进行了优化后,在生产环境取得了很好的效果,使得 Cube 的构建过程整体减少了 20% 的时间,同时很少出现作业无法启动的情况。

第二个优化就是数据量预估,之前我们遇到一个问题,就是 Kylin 生成的表比较多。Cube 构建后需要对数据量预估,根据预估的结果来决定需要创建的 HBase 表的分区数,但是发现有时最终数据量并不大的情况下,创建的表分区数还比较多,使得 HBase 集群分区管理压力增大,浪费大量资源;而有时最终数量很大,但是创建的 HBase 表分区过少,导致 Kylin 查询缓慢。这是由于默认 Cube 数据量预估算法预估出来的数据量和实际的数据量存在较大偏差,导致 Kylin 创建的 HBase 表分区数大部分情况下不合理。

在估算总数据量时,总条目数的估算误差较小,单是对单条长度的估算偏差较大。对于维度值的长度和普通数据类型的度量值长度值都可以根据数据类型确定,但是对于特殊数据类型的度量,比如度量类型为 BitMap 和 HyperLogLog,估算其长度时就会有一些问题了:

BitMap:用于对度量基数(count distinct)的精确计算,其长度大小由度量的基数决定,由于度量基数无法提前确定,导致无法获取 BitMap 的实际长度值。Kylin 采取折中办法,估算时使用一个固定值 8 k,如果对应度量基数很高,其大小很容易超过 8 k,就会导致单行预估的长度偏低。

HyperLogLog:用于对度量基数(count distinct)非精确计算,其长度是由用户选择的精度来决定的。如果误差率控制在1.22%以内,那么HyperLogLog会使用64k的空间来存储数据,在支持压缩的情况下,实际的长度会小于64k,而且如果度量基数较小,HyperLogLog存储的数据会比较稀疏,压缩效果会更好,这就导致单行预估的长度偏高。

我们解决的办法是使用已有 Segment 数据,预估新建的 Segment 数据量,算法如下图所示。基本思路是同一个 Cube 用最近一个 Segment 的统计数据来预估当前 Segment 的总数据量,统计数据包括最近一个 Segment 对应 Hive 表分区的输入记录数(InputRowsCounts),最终存储到 HBase 的实际大小 (HtableSize),然后计算出每行输入记录对应的数据大小,将这个大小作为新 Segment 的每行数据大小,并乘以新 Segment 的 Hive 表分区输入记录数,将这个数作为新 Segment 最终的数据量大小。

通过使用新的预估算法,能有效将 HBase 表分区个数误差由 >50 降至 <=1,从而避免了创建过多或者过少的分区,使得 Kylin 查询性能更加稳定,同时减小了 HBase 集群的分区管理压力。

在我们的旧版本里也做了其他的优化,比如说全局字典频繁换进换出,在 Kylin v2.5 里面也解决了。

02

版本升级

前面讲到我们典型的优化,前面的优化是 v1.5.3 的。我们最近在迁移一些项目,从另外一个部门把一些Cube 迁移到我们这里,发现 v1.5.3 存在一些问题:有些语法或者说函数不支持,全局字典构建时容易损坏,不支持用户留存分析,一个 Cube 不能并行构建多个 Segments 数据。我们就调研了 v2.6,v2.6 的新功能或特性如下图,基本解决我们在 v1.5.3 上遇到的问题。

我们上线 v2.6 后,目前已经迁移或者说正在迁移的有 20 个 Cube,稳定运行 1 个月。我们在引入 v2.6 的时候,进行了一些优化,比如说版本兼容修改、读写分离等,Spark 构建和自动剪枝我们也都在尝试之中。

目前已运行了将近 1 个月,还是很稳定的,如果大家能够升级的话,最好升级到新版本。

03

案例分享

我来分享一个案例。项目背景是我们有一个推荐系统,在 APP、PC、M 三端上做推荐位的推荐,这个推荐会涉及一些迭代,或者说涉及到样式的变更和不断的优化,这些优化的效果要进行对比。在很早以前我们用的是 Hive+MySQL 这样的方式去实现对比,分析的效果不是很好,后面就改成 Kylin,并重构了这套效果评价系统,效果非常好,大大节约了人力,开发效率也提高了。

这个是系统数据流程图,分 2 个数据,1 个是曝光,1 个是点击。这 2 个数据,我们这边是有 2 条线,1 条是通过 Kafka 来做实时计算,下面做一些可视化和监控,这两部分数据有一部分是实时的,有一部分是离线的。离线的,主要是通过 Hive 来做 ETL,最终到 Kylin 里面去做一些预计算,把数据存在 HBase 里边,我们很多页面或者说文件报表可视化的部分,就从 Kylin 里面去看推荐效果。

这里的 2 个数据,曝光的话,和点击在维度和度量上,其实很相似的。我们就把曝光和点击做了一个抽象,抽象出 15 个维度,5 个度量,比如说日期、平台、业务分类,还有推荐的场景和推荐的位置、排序算法号和召回策略号。度量有 5 个,主要算 PV 和 UV。

我们在 Kylin 里边,针对点击和曝光数据去构建了 Cube。然后就是对 Cube 的优化,在 Kylin 里面有一个很重要的就是减枝优化。如果不减枝优化,15 个维度组合数会达到 32768 个。我们根据查询样式和查询的特点做了一些优化,比如说把分区日期作为一个必要维度。然后还做了层级优化,一级路径、二级路径,这个涉及到层级关系。第三个是场景,也是做了层级优化。最后是做了组合的优化,我们后面有5个维度,在 SQL 里要么出现,要么不出现,就把它们配置成一个 aggregation group,这样 cuboid 数就少了很多。

通过 Kylin 的使用,就解决了我们推荐效果评估的难题,通过我们的优化发现我们的组合数会少很多。目前曝光 Cube 和点击 Cube 的数据量分别为 3 T 和 2 T,各有 110 亿+ 和 70 亿+ 的记录数。

这个就是我们的推荐效果评估的前瞻页面,前面有很多的选择,最后出现的是曝光 PV 的曲线图。

目前其实在 58 主要有 2 个版本的 Kylin,一个是 v1.5.3,一个是 v2.6。我们在 v1.5.3做了很多的修改和优化,整个 Cube 的规模有 400 多个,Cube 的数据量就 500+ T。我们这边的 Kylin 主要是用于离线,这边每天新增的数据有 450 G,将近有 120 亿条数据记录,生成 900 G 的数据到 HBase 里边。我们这边的查询量不是很大,主要是做内部的报表分析,不会用到很频繁的场景。我们这边基于 Kylin的查询,95% 的延迟小于 0.5 s,99% 的延迟小于 3 s,覆盖的业务线很广的,包括搜索、推荐、招聘、房产等 20 多个业务线。v2.6 正在迁的或者说已经迁的有将近 20 个,运行时间将近 1 个月。

04

Q&A

提问:总 Cube 不是 400 个嘛?迁到 v2.6 之后是 20 个?

回答:20 多个 Cube 是从其他部门维护的 Kylin 上迁移过来的,不是我们旧版本 v1.5.3 迁移过来的。

提问:400个 Cube,数据量大的话,你们这边一天构建的时间是多少?

回答:单个Cube总数据量有 84 T 的,这个 Cube 一天构建时间稍微长一点,大概要 4-5 个小时。

提问:这么快,你们用的是 MR?

回答:用的是 MR。我们新版本在测试 Spark 构建。

提问:更新是增量更新还是全量更新?

回答:我们是增量更新,不是全量更新。

提问:我想问一下流量分析是漏斗分析吗?

回答:对,是漏斗分析。

添加企微

kyligence
关注我们

kyligence