海量实时计算(Ad-Hoc Query) 概览

文章部分内容已经 “过时”,尤其是组件选型方面,综述部分可供参考

什么是海量实时计算?

更常见的叫法是 Ad-Hoc Query,在人可容忍的交互时间内(5秒内),对于给定查询返回结果,典型应用场景为:

  • 产品运营实时洞察
  • A/B Testing
  • 用户增长领域

查询有以下特点:

  • (灵活性) 查询方式不固定,不能被预计算
  • (海量) 查询数据规模超出传统数据库可以承受的规模 (100亿+)
  • (实时) 可以查询到最近产生的数据, 这一点不同业务形态要求的实时性会有差别

Ad-Hoc Query 在很多方面和 OLAP 很像,只是要求更高的灵活性

难点在哪里

灵活性,海量,实时 这三个维度像是 CAP 理论里面的三角关系一样,如果同时满足会有一些难度,我们分别讨论一下对应的方案。

  • 如果仅考虑 海量,实时 两个维度,可选方案可以是 Druid, Flink+ES,但是这些预计算类型方案没有灵活性
  • 如果仅考虑 灵活性, 实时 两个维度,没有海量的约束,就不用考虑横向扩展/成本问题,解决方案就非常多元化了,例如传统数据库,ElasticSearch, ClickHourse 等
  • 如果仅考虑 灵活性, 海量 两个维度,可选方案是 HDFS + ORC + Spark/Presto/Impala

目前没有开源解决方案可以直接比较好的处理好这个三角关系,ElasticSearch 可能是最接近的开源方案,但是成本是非常严重的一个问题,查询能力稍弱,是 SQL 子集。

这里会讨论一些组合方案,以达到海量实时计算目的。

查询模型

  • Filter,从海量数据中选择符合条件的一小部分数据集 (通常1万条以下)
  • Aggregation, 聚合是数据分析中最常见的任务,简单来说就是 Group By
  • Custom Model, 自定义查询模型, 例如生成漏斗模型,桑吉模型 等等,这需要查询框架提供 UDF 支持

列式存储

列式存储在 Ad-Hoc Query 中非常重要,因为有实时性要求,需要尽可能减少磁盘/网络 IO

目前场景的列式存储为 Parquet,ORC,这两者经常会被作为比较对象

  • Benchmarking PARQUET vs ORC
  • Which Hadoop File Format Should I Use?

我个人的看法:

  • ORC 比 Parquet 晚一点出现,在数据压缩上有一定优势
  • Parquet 出现的早,开源社区的支持度会比 ORC 好一点,但已经非常接近了
  • Parquet 和 ORC 背后是 Cloudera(Impala) 和 Hortonworks(Hadoop,hive) 两家商业公司之争
  • 最近阿里巴巴也加入了 ORC 阵营,并做了成吨的优化
  • ORC 前景看好一些

另外除了 Parquet,ORC,也有一些建立在其之上的存储框架,例如Spark社区全力打造的 DeltaLake 以及我们下文会重点介绍的 Kudu, 这两者均构建在 Parquet 格式之上

另外关于列式存储的起源,Google 2010 年发布的一篇论文(Google 内部在 2006年已经投入生产环境使用), 大数据领域的小伙伴想必不陌生: Dremel: Interactive Analysis of Web-Scale Datasets

Dremel 中提到的存储方案,开源实现便是 Parquet, 在 Google 内部的直系后代是 Capacitor, 之后便作为 Big Query 的内置存储格式, 关于 Big Query 的更多介绍可以参考 A Deep Dive Into Google BigQuery Architecture

实时性

大数据在实时性方面一直没有太多诉求,但是商业分析越来越讲究时效性,例如要评估一个广告投放的效果并进行调整,就需要尽量快的进行分析和数据反馈。

而实效性与列式存储是有矛盾的。

列式存储如 ORC/Parquet 核心思想都是把相似的数据放在一起,期望查询的时候可以直接忽略不满足Where条件的数据块,而实效性又要求数据需要可以实时刷写并用于查询。这种矛盾就引发我们去实现定时的 ETL 过程去做数据重整,而这部分复杂性最好是应当由某个存储组件去承担。如果要求秒极的实时性,必然是经过数据重整的数据和零散的近期数据结合在一起做查询,进一步增加了复杂性。

大数据计算引擎和存储引擎完全解耦,中间的定期 Table Compaction 成了一个空白地带,因为这需要一个常驻的后台服务。可以选择 Spark 去实现 Table Compaction,毕竟只需要几行代码,但从工程实践的角度讲,引入一个流程从来都不是几行代码这么简单,需要解决的问题有很多:

  • 如何定时跑
  • 表结构变更怎么办
  • Compaction 和最近的实时数据如何关联查询

如果选择 Spark Compaction,关于加载实时数据的方案可能是,额外写一份数据到 HBase/ElasticSearch,这样可以历史数据从 HDFS ORC 加载,实时数据从 HBase/ElasticSearch 加载。

但是其实还会有一些问题:

  • HBase 适合做全量 Scan,但是对谓词下推(PushDown) 支持比较薄弱,这实际上有IO的浪费,看业务场景是否可以容忍这种 IO 浪费
  • ElasticSearch 显然对 谓词下推(PushDown) 可以完美支持,但是 ElasticSearch 与 Spark 间的吞吐有非常大的问题,单机大约只能到几万记录每秒的速度

业界 Parquet/ORC 与 HBase 结合满足实时性需求的案例比较多。

Kudu 值得单独介绍一下

Apache Kudu 为了解决海量数据的实时性查询而生,它不像 HBase 那样专注于 OLTP,而是取得了 OLAP/OLTP 的一个平衡点。下面一张图可以形象说明这一点:

下面这些特性使 Kudu 极其适合于 Ad-Hoc Query

  • 实时性,数据可以实时写入,先内存缓存再落盘,查询时会结合内存和磁盘的结果
  • 列式存储(Parquet底层),支持谓词下推
  • 有主键,意味着可以进行非聚合查询 (示例场景: 查询一个用户的所有事件)

但是 Kudu 也有显而易见的缺点:

  • 字段大小 64K 字节限制
  • 不支持 Map/Struct/Array 等复合数据类型
  • 分区体验不友好,需要定时任务维护分区

查询引擎

查询引擎,目前大都是 MPP 架构查询引擎,例如 Impala/Presto/Drill/Vertica/ClickHouse,Spark 是 MapReduce 阵营里唯一可用于 Ad-Hoc Query 的引擎。

在一般的评测里,都会给予 Impala 的性能评分,而 Presto 紧随其后,Drill 目前在国内目前鲜有落地案例,Vertica 是付费产品,一般在传统企业中使用的多,Spark 性能中等,而 ClickHouse 在单表查询中有压倒性优势。

下面是一些简要的对比:

ImpalaPrestoClickHouseSpark单表速度4453多表速度5433社区活跃度3415业界应用案例3425灵活性3325UDF 支持3315

ClickHouse 存储计算是一体的,其他的都是单纯的计算框架

Impala 可参考论文: Impala: A Modern, Open-Source SQL Engine for Hadoop

Presto 可参考论文: Presto: SQL on Everything

ClickHouse 可参考官方文档: Overview of ClickHouse Architecture

Spark 可参考: The Internals Of Apache Spark

Impala

Impala + Kudu 组合,在腾讯和神策都被选为实时分析的承载引擎。

Impala 的快速有口皆碑,主要得益于:

  • 优化查询计划
  • 支持列式存储
  • HDFS short-circuit local reads
  • Runtime Code Generation
  • C++ 本身性能优势

Presto

如果需要多数据源关联查询,使用 Presto 是不错的选择。

但对于 Ad-Hoc Query 这种对性能要求严苛的场景,存储实际上没有那么多选择。

Spark

Spark 本身提供的灵活性远超其他的查询引擎,相应的速度方面就不能进行太多的定制性优化。Spark 如果仅用于 SQL 查询时,可以使用 Spark Thrift JDBC/ODBC server, 但其实 Spark 在纯SQL领域方面相比 Impala/Presto 里面没有太大优势,并且查询时间受数据倾斜影响非常大。

但涉及到复杂计算模型时,Spark 在易用性方面有非常大优势。要发挥 Spark 全部能力,目前比较好的解决方案是 Spark + Hydrosphere Mist, 众所周知,Spark 任务加载需要一段固定的时长,因此绝不能查询到来时候去加载 Spark 应用,而需要预先加载。但是预先加载长期 Hold 住计算资源也会造成一些浪费,Mist 完美的解决了这些问题,可以预先加载 Spark 应用,并在长时间没有新的查询时候关闭 Spark Worker。

ClickHouse

ClickHouse 在头条有比较深度的落地,但是对应的头条团队也进行了成吨的优化。建议小团队谨慎使用,优先选项社区成熟的组件。

最终方案分类

以下方案均以海量为前提,但是在灵活性和实时性方面有取舍。

方案 1: ElasticSearch

适用场景: 低查询耗时,实时,低灵活性,高成本 (土豪的选择)

方案 2: Spark + HDFS + ORC/Parquet

适用场景: 高查询耗时,非实时,高灵活性,低成本 (需要有 Table Compaction 流程)

方案 3: Impala + Kudu

适用场景: 低查询耗时,实时,低灵活性,低成本 (平衡之选)

方案 4: Impala + Alluxio + HDFS + ORC/Parquet

适用场景: 低查询耗时,非实时,低灵活性,高成本 (极致的快速)

方案 5: Druid

适用场景: 低查询耗时,实时,极低灵活性,高成本 (一般用于秒级聚合查询)

方案 6: Apache Kylin + Spark

适用场景: 低查询耗时,非实时,极低灵活性,低成本 (不要求实时的话,低成本方案)

方案 7: Spark + Kudu

适用场景: 高查询耗时,实时,高灵活性,低成本 (实时+灵活性的最佳选择,但性能会稍差)

总结

这里是一些海量实时计算领域的一些概览,后面会有一系列文章,详细说明在工程实践中的落地

求订阅专栏 :)

举报
评论 0