OLAP系统解析:Apache Kylin和Baidu Palo哪家强?

做者 | 康凯森
编辑 | Debra
AI 前线导读:Apache Kylin 和 Baidu Palo 都是优秀的开源 OLAP 系统,本文将全方位地对比 Kylin 和 Palo。Kylin 和 Palo 分别是 MOALP 和 ROLAP 的表明,对比这两个系统的目的不是为了说明哪一个系统更好,只是为了明确每一个系统的设计思想和架构原理,让你们能够根据本身的实际需求去选择合适的系统,也能够进一步去思考咱们如何去设计出更优秀的 OLAP 系统。

本文对 Apache Kylin 的理解基于近两年来在生产环境大规模地使用,运维和深度开发,我已向 Kylin 社区贡献了 98 次 Commit,包含多项新功能和深度优化。本文对 Baidu Palo 的理解基于官方文档和论文的阅读,代码的粗浅阅读和较深刻地测试。

更多干货内容请关注微信公众号“AI 前线”,(ID:ai-front)
  • 1 系统架构html

    • 1.1 What is Kylinjava

    • 1.2 What is Palogit

  • 2 数据模型github

    • 2.1 Kylin 的聚合模型算法

    • 2.2 Palo 的聚合模型sql

    • 2.3 Kylin Cuboid VS Palo RollUpapache

    • 2.4 Palo 的明细模型微信

  • 3 存储引擎数据结构

  • 4 数据导入架构

  • 5 查询

  • 6 精确去重

  • 7 元数据

  • 8 高性能

  • 9 高可用

  • 10 可维护性

    • 10.1 部署

    • 10.2 运维

    • 10.3 客服

  • 11 易用性

    • 11.1 查询接入

    • 11.2 学习成本

    • 11.3 Schema Change

  • 12 功能

  • 13 社区和生态

  • 14 总结

  • 15 参考资料

注: 本文的对比 基于 Apache Kylin 2.0.0 和 Baidu Palo 0.8.0。

1 系统架构
1.1 What is Kylin

Kylin 的核心思想是预计算,利用空间换时间来 加速查询模式固定的 OLAP 查询。

Kylin 的理论基础是 Cube 理论,每一种维度组合称之为 Cuboid,全部 Cuboid 的集合是 Cube。 其中由全部维度组成的 Cuboid 称为 Base Cuboid,图中 (A,B,C,D) 即为 Base Cuboid,全部的 Cuboid 均可以基于 Base Cuboid 计算出来。 在查询时,Kylin 会自动选择知足条件的最“小”Cuboid,好比下面的 SQL 就会对应 Cuboid(A,B):

select xx from table where A=xx group by B


Kylin-cube

下图是 Kylin 数据流转的示意图,Kylin 自身的组件只有两个:JobServer 和 QueryServer。 Kylin 的 JobServer 主要负责将数据源(Hive,Kafka)的数据经过计算引擎(MapReduce,Spark)生成 Cube 存储到存储引擎(HBase)中;QueryServer 主要负责 SQL 的解析,逻辑计划的生成和优化,向 HBase 的多个 Region 发起请求,并对多个 Region 的结果进行汇总,生成最终的结果集。


kylin-data

下图是 Kylin 可插拔的架构图, 在架构设计上,Kylin 的数据源,构建 Cube 的 计算引擎,存储引擎都是可插拔的。Kylin 的核心就是这套可插拔架构,Cube 数据模型和 Cuboid 的算法。


Kylin

1.2 What is Palo

Palo 是一个基于 MPP 的 OLAP 系统,主要整合了 Google Mesa(数据模型),Apache Impala(MPP Query Engine) 和 Apache ORCFile(存储格式,编码和压缩) 的技术。


baidu-palo

Palo 的系统架构以下,Palo 主要分为 FE 和 BE 两个组件,FE 主要负责查询的编译,分发和元数据管理(基于内存,相似 HDFS NN);BE 主要负责查询的执行和存储系统。


baidu-palo

2 数据模型
2.1 Kylin 的聚合模型

Kylin 将表中的列分为维度列和指标列。在数据导入和查询时相同维度列中的指标会按照对应的聚合函数 (Sum, Count, Min, Max, 精确去重,近似去重,百分位数,TOPN) 进行聚合。

在存储到 HBase 时,Cuboid+ 维度 会做为 HBase 的 Rowkey, 指标会做为 HBase 的 Value,通常全部指标会在 HBase 的一个列族,每列对应一个指标,但对于较大的去重指标会单独拆分到第 2 个列族。


Kylin-model

2.2 Palo 的聚合模型

Palo 的聚合模型借鉴自 Mesa,但本质上和 Kylin 的聚合模型同样,只不过 Palo 中将维度称做 Key,指标称做 Value。


palo-data-model

Palo 中比较独特的聚合函数是 Replace 函数,这个聚合函数可以保证相同 Keys 的记录只保留最新的 Value, 能够借助这个 Replace 函数来实现 点更新。通常 OLAP 系统的数据都是只支持 Append 的,可是像电商中交易的退款,广告点击中的无效点击处理,都须要去更新以前写入的单条数据,在 Kylin 这种没有 Relpace 函数的系统中咱们必须把包含对应更新记录的整个 Segment 数据所有重刷,可是有了 Relpace 函数,咱们只须要再追加 1 条新的记录便可。 可是 Palo 中的 Repalce 函数有个缺点:没法支持预聚合, 就是说只要你的 SQL 中包含了 Repalce 函数,即便有其余能够已经预聚合的 Sum,Max 指标,也必须现场计算。

为何 Palo 能够支持点更新呢?

Kylin 中的 Segment 是不可变的,也就是说 HFile 一旦生成,就再也不发生任何变化。可是 Palo 中的 Segment 文件和 HBase 同样,是能够进行 Compaction 的,具体能够参考 Google Mesa 论文解读中的 Mesa 数据版本化管理(https://blog.bcmeng.com/post/google-mesa.html#mesa%E6%95%B0%E6%8D%AE%E7%89%88%E6%9C%AC%E5%8C%96%E7%AE%A1%E7%90%86)

Palo 的聚合模型相比 Kylin 有个缺点:就是一个 Column 只能有一个预聚合函数,没法设置多个预聚合函数。 不过 Palo 能够现场计算其余的聚合函数。 Baidu Palo 的开发者 Review 时提到,针对这个问题,Palo 还有一种解法:因为 Palo 支持多表导入的原子更新,因此 1 个 Column 须要多个聚合函数时,能够在 Palo 中建多张表,同一份数据导入时,Palo 能够同时原子更新多张 Palo 表,缺点是多张 Palo 表的查询路由须要应用层来完成。

Palo 中和 Kylin 的 Cuboid 等价的概念是 RollUp 表,Cuboid 和 RollUp 表均可以认为是一种 Materialized Views 或者 Index。 Palo 的 RollUp 表和 Kylin 的 Cuboid 同样,** 在查询时不须要显示指定,系统内部会根据查询条件进行路由。 以下图所示:


Palo Rollup

Palo 中 RollUp 表的路由规则以下:

  1. 选择包含全部查询列的 RollUp 表

  2. 按照过滤和排序的 Column 筛选最符合的 RollUp 表

  3. 按照 Join 的 Column 筛选最符合的 RollUp 表

  4. 行数最小的

  5. 列数最小的

2.3 Kylin Cuboid VS Palo RollUp


Kylin cuboid vs palo rollup

2.4 Palo 的明细模型

因为 Palo 的聚合模型存在下面的缺陷,Palo 引入了明细模型。

  • 必须区分维度列和指标列

  • 维度列不少时,Sort 的成本很高

  • Count 成本很高,须要读取全部维度列(能够参考 Kylin 的解决方法进行优化)

Palo 的明细模型不会有任何聚合,不区分维度列和指标列,可是在建表时须要指定 Sort Columns,数据导入时会根据 Sort Columns 进行排序,查询时根据 Sort Column 过滤会比较高效。

以下图所示,Sort Columns 是 Year 和 City。


Kylin-detail-model

这里须要注意一点,Palo 中一张表只能有一种数据模型,即要么是聚合模型,要么是明细模型,并且 Roll Up 表的数据模型必须和 Base 表一致, 也就是说明细模型的 Base 表不能有聚合模型的 Roll Up 表。

3 存储引擎

Kylin 存储引擎 HBase:


如上图所示,在 Kylin 中 1 个 Cube 能够按照时间拆分为多个 Segment,Segment 是 Kylin 中数据导入和刷新的最小单位。Kylin 中 1 个 Segment 对应 HBase 中一张 Table。 HBase 中的 Table 会按照 Range 分区拆分为多个 Region, 每一个 Region 会按照大小拆分为多个 HFile。

关于 HFile 的原理网上讲述的文章已经不少了,我这里简单介绍下。首先 HFile 总体上能够分为元信息,Blcoks,Index3 部分,Blcoks 和 Index 均可以分为 Data 和 Meta 两部分。Block 是数据读取的最小单位,Block 有多个 Key-Value 组成,一个 Key-Value 表明 HBase 中的一行记录,Key-Value 由 Kylin-Len,Value-Len,Key-Bytes,Value-Bytes 4 部分组成。更详细的信息你们能够参考下图 (下图来源于互联网,具体出处不详):


HBase-HFile

Palo 存储引擎:


如上图所示,Palo 的 Table 支持二级分区,能够先按照日期列进行一级分区,再按照指定列 Hash 分桶。具体来讲,1 个 Table 能够按照日期列分为多个 Partition, 每一个 Partition 能够包含多个 Tablet,Tablet 是数据移动、复制等操做的最小物理存储单元, 各个 Tablet 之间的数据没有交集,而且在物理上独立存储。Partition 能够视为逻辑上最小的管理单元,数据的导入与删除,仅能针对一个 Partition 进行。1 个 Table 中 Tablet 的数量 = Partition num * Bucket num。Tablet 会按照必定大小(256M)拆分为多个 Segment 文件,Segment 是列存的,可是会按行(1024)拆分为多个 Rowblock。


palo segment file

下面咱们来看下 Palo Segment 文件的具体格式,Palo 文件格式主要参考了 Apache ORC。如上图所示,Palo 文件主要由 Meta 和 Data 两部分组成,Meta 主要包括文件自己的 Header,Segment Meta,Column Meta,和每一个 Column 数据流的元数据,每部分的具体内容你们看图便可,比较详细。 Data 部分主要包含每一列的 Index 和 Data,这里的 Index 指每一列的 Min,Max 值和数据流 Stream 的 Position;Data 就是每一列具体的数据内容,Data 根据不一样的数据类型会用不一样的 Stream 来存储,Present Stream 表明每一个 Value 是不是 Null,Data Stream 表明二进制数据流,Length Stream 表明非定长数据类型的长度。 下图是 String 使用字典编码和直接存储的 Stream 例子。


Palo String encoding

下面咱们来看下 Palo 的前缀索引:


Palo index

本质上,Palo 的数据存储是相似 SSTable(Sorted String Table)的数据结构。该结构是一种有序的数据结构,能够按照指定的列有序存储。在这种数据结构上,以排序列做为条件进行查找,会很是的高效。而前缀索引,即在排序的基础上,实现的一种根据给定前缀列,快速查询数据的索引方式。前缀索引文件的格式如上图所示,索引的 Key 是每一个 Rowblock 第一行记录的 Sort Key 的前 36 个字节,Value 是 Rowblock 在 Segment 文件的偏移量。

有了前缀索引后,咱们查询特定 Key 的过程就是两次二分查找:

  1. 先加载 Index 文件,二分查找 Index 文件获取包含特定 Key 的 Row blocks 的 Offest, 而后从 Sement Files 中获取指定的 Rowblock;

  2. 在 Rowblocks 中二分查找特定的 Key

4 数据导入

Kylin 数据导入:


Kylin data loading

如上图,Kylin 数据导入主要分为建 Hive 大宽表 (这一步会处理 Join);维度列构建字典;逐层构建 Cuboid;Cuboid 转为 HFile;Load HFile To HBase; 元数据更新这几步。

其中 Redistribute 大宽表这一步的做用是为了将整个表的数据搞均匀,避免后续的步骤中有数据倾斜,Kylin 有配置能够跳过这一步。

其中 Extract Distinct Columns 这一步的做用是获取须要构建字典的维度列的 Distinct 值。假如一个 ID 维度列有 1,2,1,2,2,1,1,2 这 8 行,那么通过这一步后 ID 列的值就只有 1,2 两行,作这一步是为了下一步对维度列构建字典时更快速。

其余几个步骤都比较好理解,我就再也不赘述。更详细的信息能够参考 Apache Kylin Cube 构建原理(https://blog.bcmeng.com/post/kylin-cube.html)

Palo 数据导入:


palo data loading

Palo 数据导入的两个核心阶段是 ETL 和 LOADING, ETL 阶段主要完成如下工做:

  • 数据类型和格式的校验

  • 根据 Teblet 拆分数据

  • 按照 Key 列进行排序, 对 Value 进行聚合

LOADING 阶段主要完成如下工做:

  • 每一个 Tablet 对应的 BE 拉取排序好的数据

  • 进行数据的格式转换,生成索引LOADING 完成后会进行元数据的更新。

5 查询

Kylin 查询:


Kylin query

如上图,整个 Kylin 的查询过程比较简单,是个 Scatter-Gather 的模型。图中圆形框的内容发生在 Kylin QueryServer 端,方形框的内容发生在 HBase 端。Kylin QueryServer 端收到 SQL 后,会先进行 SQL 的解析,而后生成和优化 Plan,再根据 Plan 生成和编译代码,以后会根据 Plan 生成 HBase 的 Scan 请求,若是可能,HBase 端除了 Scan 以外,还会进行过滤和聚合(基于 HBase 的 Coprocessor 实现),Kylin 会将 HBase 端返回的结果进行合并,交给 Calcite 以前生成好的代码进行计算。

Palo 查询:


palo-impala-query

Palo 的查询引擎使用的是 Impala,是 MPP 架构。 Palo 的 FE 主要负责 SQL 的解析,语法分析,查询计划的生成和优化。查询计划的生成主要分为两步:

  1. 生成单节点查询计划 (上图左下角)

  2. 将单节点的查询计划分布式化,生成 PlanFragment(上图右半部分)

第一步主要包括 Plan Tree 的生成,谓词下推, Table Partitions pruning,Column projections,Cost-based 优化等;第二步 将单节点的查询计划分布式化,分布式化的目标是 最小化数据移动和最大化本地 Scan,分布式化的方法是增长 ExchangeNode,执行计划树会以 ExchangeNode 为边界拆分为 PlanFragment,1 个 PlanFragment 封装了在一台机器上对同一数据集的部分 PlanTree。如上图所示:各个 Fragment 的数据流转和最终的结果发送依赖:DataSink。

当 FE 生成好查询计划树后,BE 对应的各类 Plan Node(Scan, Join, Union, Aggregation, Sort 等)执行本身负责的操做便可。

6 精确去重

Kylin 的精确去重:

Kylin 的精确去重是基于全局字典和 RoaringBitmap 实现的基于预计算的精确去重。具体能够参考 Apache Kylin 精确去重和全局字典权威指南(https://blog.bcmeng.com/post/kylin-distinct-count-global-dict.html)

Palo 的精确去重:

Palo 的精确去重是现场精确去重,Palo 计算精确去重时会拆分为两步:

  1. 按照全部的 group by 字段和精确去重的字段进行聚合

  2. 按照全部的 group by 字段进行聚合

下面是个简单的等价转换的例子:



Palo 现场精确去重计算性能和 去重列的基数、去重指标个数、过滤后的数据大小成负相关;

7 元数据

Kylin 的元数据 :

Kylin 的元数据是利用 HBase 存储的,能够很好地横向扩展。Kylin 每一个具体的元数据都是一个 Json 文件,HBase 的 Rowkey 是文件名,Value 是 Json 文件的内容。Kylin 的元数据表设置了 IN_MEMORY => 'true' 属性, 元数据表会常驻 HBase RegionServer 的内存,因此元数据的查询性能很好,通常在几 ms 到几十 ms。

Kylin 元数据利用 HBase 存储的一个问题是,在 Kylin 可插拔架构下,即便咱们实现了另外一种存储引擎,咱们也必须部署 HBase 来存储元数据,因此 Kylin 要真正作到存储引擎的可插拔,就必须实现一个独立的元数据存储。

Palo 的元数据:

Palo 的元数据是基于内存的,这样作的好处是性能很好且不须要额外的系统依赖。 缺点是单机的内存是有限的,扩展能力受限,可是根据 Palo 开发者的反馈,因为 Palo 自己的元数据很少,因此元数据自己占用的内存不是不少,目前用大内存的物理机,应该能够支撑数百台机器的 OLAP 集群。 此外,OLAP 系统和 HDFS 这种分布式存储系统不同,咱们部署多个集群的运维成本和 1 个集群区别不大。

关于 Palo 元数据的具体原理你们能够参考 Palo 官方文档 Palo 元数据设计文档(https://github.com/baidu/palo/wiki/Metadata-Design)

8 高性能

Why Kylin Query Fast:


Kylin query

Kylin 查询快的核心缘由就是预计算,如图 (图片出处 Apache kylin 2.0: from classic olap to real-time data warehouse https://www.slideshare.net/YangLi43/apache-kylin-20-from-classic-olap-to-realtime-data-warehouse),Kylin 现场查询时不须要 Join,也几乎不须要聚合,主要就是 Scan + Filter。

Why Palo Query Fast:

  1. In-Memory Metadata。 Palo 的元数据就在内存中,元数据访问速度很快。

  2. 聚合模型能够在数据导入时进行预聚合。

  3. 和 Kylin 同样,也支持预计算的 RollUp Table。

  4. MPP 的查询引擎。

  5. 向量化执行。相比 Kylin 中 Calcite 的代码生成,向量化执行在处理高并发的低延迟查询时性能更好,Kylin 的代码生成自己可能会花费几十 ms 甚至几百 ms。

  6. 列式存储 + 前缀索引。

9 高可用

Kylin 高可用:

Kylin JobServer 的高可用: Kylin 的 JobServer 是无状态的,一台 JobServer 挂掉后,其余 JobServer 会很快接管正在 Running 的 Job。JobServer 的高可用是基于 Zookeeper 实现的,具体能够参考 Apache Kylin Job 生成和调度详解(https://blog.bcmeng.com/post/kylin-job.html)。

Kylin QueryServer 的高可用:Kylin 的 QueryServer 也是无状态的,其高可用通常经过 Nginx 这类的负载均衡组件来实现。

Kylin Hadoop 依赖的高可用: 要单纯保证 Kylin 自身组件的高可用并不困难,可是要保证 Kylin 总体数据导入和查询的高可用是 十分困难的,由于必须同时保证 HBase,Hive,Hive Metastore,Spark,Mapreduce,HDFS,Yarn,Zookeeper,Kerberos 这些服务的高可用。

Palo 高可用:

Palo FE 的高可用: Palo FE 的高可用主要基于 BerkeleyDB java version 实现,BDB-JE 实现了类 Paxos 一致性协议算法。

Palo BE 的高可用:Palo 会保证每一个 Tablet 的多个副本分配到不一样的 BE 上,因此一个 BE down 掉,不会影响查询的可用性。

10 可维护性
10.1 部署

Kylin 部署: 若是彻底从零开始,你就须要部署 1 个 Hadoop 集群和 HBase 集群。 即便公司已经有了比较完整的 Hadoop 生态,在部署 Kylin 前,你也必须先部署 Hadoop 客户端,HBase 客户端,Hive 客户端,Spark 客户端。

Palo 部署: 直接启动 FE 和 BE。

10.2 运维

Kylin 运维: 运维 Kylin 对 Admin 有较高的要求,首先必须了解 HBase,Hive,MapReduce,Spark,HDFS,Yarn 的原理;其次对 MapReduce Job 和 Spark Job 的问题排查和调优经验要丰富;而后必须掌握对 Cube 复杂调优的方法;最后出现问题时排查的链路较长,复杂度较高。

Palo 运维: Palo 只须要理解和掌握系统自己便可。

10.3 客服

Kylin 客服: 须要向用户讲清 Hadoop 相关的一堆概念;须要教会用户 Kylin Web 的使用;须要教会用户如何进行 Cube 优化(没有统一,简洁的优化原则);须要教会用户怎么查看 MR 和 Spark 日志;须要教会用户怎么查询;

Palo 客服: 须要教会用户聚合模型,明细模型,前缀索引,RollUp 表这些概念。

11 易用性
11.1 查询接入

Kylin 查询接入:Kylin 支持 Htpp,JDBC,ODBC 3 种查询方式。

Palo 查询接入: Palo 支持 Mysql 协议,现有的大量 Mysql 工具均可以直接使用,用户的学习和迁移成本较低。

11.2 学习成本

Kylin 学习成本: 用户要用好 Kylin,须要理解如下概念:

  • Cuboid

  • 汇集组

  • 强制维度

  • 联合维度

  • 层次维度

  • 衍生维度

  • Extend Column

  • HBase RowKey 顺序

此外,前面提到过,用户还须要学会怎么看 Mapreduce Job 和 Spark Job 日志。

Palo 学习成本: 用户须要理解聚合模型,明细模型,前缀索引,RollUp 表这些概念。

11.3 Schema Change

Schema 在线变动是一个十分重要的 feature,由于在实际业务中,Schema 的变动会十分频繁。

Kylin Schema Change: Kylin 中用户对 Cube Schema 的任何改变,都须要在 Staging 环境重刷全部数据,而后切到 Prod 环境。整个过程周期很长,资源浪费比较严重。

Palo Schema Change:Palo 支持 Online Schema Change。

所谓的 Schema 在线变动就是指 Scheme 的变动不会影响数据的正常导入和查询,Palo 中的 Schema 在线变动有 3 种:

  • direct schema change:就是重刷全量数据,成本最高,和 kylin 的作法相似。当修改列的类型,稀疏索引中加一列时须要按照这种方法进行。

  • sorted schema change: 改变了列的排序方式,需对数据进行从新排序。例如删除排序列中的一列, 字段重排序。

  • linked schema change: 无需转换数据,直接完成。对于历史数据不会重刷,新摄入的数据都按照新的 Schema 处理,对于旧数据,新加列的值直接用对应数据类型的默认值填充。例如加列操做。Druid 也支持这种作法。

12 功能


Apache kylin VS baidu palo

注: 关于 Kylin 的明细查询,Kylin 自己只有聚合模型,可是也能够 经过将全部列做为维度列,只构建 Base Cuboid 来实现明细查询, 缺点是效率比较低下。

注: 虽然 Palo 能够同时支持高并发,低延迟的 OLAP 查询和高吞吐的 Adhoc 查询,但显然这两类查询会相互影响。因此 Baidu 在实际应用中也是用两个集群分别知足 OLAP 查询和 Adhoc 查询需求。

13 社区和生态

Palo 社区刚刚起步,目前核心用户只有 Baidu;Kylin 的社区和生态已经比较成熟,Kylin 是第一个彻底由中国开发者贡献的 Apache 顶级开源项目,目前已经在多家大型公司的生产环境中使用。

14 总结

本文从多方面对比了 Apache Kylin 和 Baidu Palo,有理解错误的地方欢迎指正。本文更多的是对两个系统架构和原理的客观描述,主观判断较少。最近在调研了 Palo,ClickHouse,TiDB 以后,也一直在思考 OLAP 系统的发展趋势是怎样的,下一代更优秀的 OLAP 系统架构应该是怎样的,一个系统是否能够同时很好的支持 OLTP 和 OLAP,这些问题想清楚后我会再写篇文章描述下,固然,你们有好的想法,也欢迎直接 Comment。

15 参考资料

1 Palo 文档和源码:https://github.com/baidu/palo

2 Kylin 源码:https://github.com/apache/kylin

3 Apache kylin 2.0: from classic olap to real-time data warehouse 在 Kylin 高性能部分引用了第 4 页 PPT 的截图:https://www.slideshare.net/YangLi43/apache-kylin-20-from-classic-olap-to-realtime-data-warehouse

4 百度 MPP 数据仓库 Palo 开源架构解读与应用 在 Palo 查询部分引用了第 31 页 PPT 的截图 https://myslide.cn/slides/6392

相关文章
相关标签/搜索