黄良懿:大数据时代的分析基础——SQL on Hadoop
大数据时代,我们对数据的应用主要有两大方面:一是以传统BI进一步演化而来的更强的业务洞察分析能力,用以辅助决策;二是以标签体系导向的精准营销和推荐系统两者为主要代表的数据挖掘产品的应用,用以在线或近线的方式直接产出业务价值。
本文主要介绍大数据时代分析方面的一些基础知识,事实上即便是BI这个已有多年积累的方向,在进入大数据时代后,依然要再度面对巨大的挑战。无论是来自原始数据的爆发式增长,还是来自于OLAP Cube难以抑制的膨胀,又或是数据多元化的新难题。
一、Why SQL on Hadoop?
对于一个分析平台,主要包含了数据收集、数据处理、数据展示三大环节。
其中数据展示环节主要依托于SQL调取数据并通过各类工具或报表平台展现。在大数据时代,由于行为数据的丰满翔实和业务的日益完善,我们可供分析、展现的维度也越来越多,日益膨胀的数据结果集迫切需要从Oracle/Infobright等数据库中迁移到Hadoop中去,以便借助分布式的能力解决性能瓶颈。而如果Hadoop生态能提供完整的SQL能力,无疑能让迁移成本降到最低。Apache Hive就是这样的一个产品,但由于需要借助Map Reduce去计算,所以几乎每个SQL都需要一定的集群调度时间,使得其性能往往只能满足批处理任务的需求而无法完成可交互查询(Interactive Query)的需求。
而数据处理环节主要就是我们常说的ETL(Extract / Transform / Load),需要对数据做解析、转换并导入到新存储层上最终供数据展示环节使用,起到一个承上启下的作用。在过去,这通常是在一台高配置的服务器上独立运行的商业工具或自研的脚本。在海量的数据的Hadoop生态系统当中,由于需要处理的量级太大,这样的策略就不再可行了,但利用MapReduce,我们仍可以较为简单的编写程序完成这样的工作。
然而在一个成熟的数据系统中,其数据处理的任务数往往是成千上万的,虽然每个Job开发都并不太复杂,但总耗时、维护成本都较为高昂。Pig是一个相对而言更好的解决方案,它提供了一个SQL-LIKE语言,其编译器会把类SQL的代码转换为经过优化处理的一系列MapReduce运算提交执行,相比编写MapReduce程序而言,无论是开发效率还是可维护性都要高很多。
然而比起SQL-LIKE来,用SQL在Hadoop生态中完成ETL处理就更为成熟了,这意味着甚至连没有开发能力的分析师都能自行开发数据处理任务,而不是填写需求说明单并苦等幸福降临。 于是我们看到,在2016年的今天,Hadoop生态中已有超过十个以上形形色色的SQL on Hadoop方案了。
另外,传统BI中以多维数据库(MOLAP)为主流,通过商业或开源工具直接支持上下钻、旋转切片等的方式在大数据时代也在更多往Adhoc Query这样的用户自行选择指标、维度、筛选条件后生成SQL执行的方式转换。综上所述,SQL是人与工具使用同一套标准与数据仓库交互的必然选择。
二、选择SQL引擎的关键要素
事实上,在批处理的时代,Hive完全是一枝独秀的,至今大部分企业大数据处理的ETL部分依然还是建立在Hive上,但在可交互查询上Hive的性能就非常抱歉了,大数据OLAP最迫切所要解决的问题就是大量实时运算导致的响应时间迟滞,于是有了像Shark / Spark SQL、Impala、Presto、Kylin等等更多的SQL引擎。在不同的业务场景下,选择合适查询引擎是决定平台建设成败的关键因素之一。
1. 兼容性
SQL引擎的使用者通常具备MySQL/Oracle的SQL经验,目前主流SQL on Hadoop解决方案的SQL兼容性支持都还有较大的提升空间。 更高的兼容性对分析师而言无异于红利,这意味着展现层甚至是处理任务上需要的改动会显著的减少。
目前的SQL on Hadoop引擎大多都能支持SQL-92的主要特性,但由于绝大部分都是面向OLAP类的只读需求,几乎都不具备ACID特性,所以对兼容性的考察更多需要考虑现有系统中所使用的各类函数是否有功能完备的支持。比如Hive中的MD5函数就在1.3.0才引入。
2. 存储方式
Hadoop支持好几种内置的存储格式,比如Parquet、Avro、ORCFile等,而Hive引入的RCFile也是一个常见的选择。根据源数据的不同选择合适的存储引擎对处理、查询的性能及存储空间都有非常大的影响。比如多个字段会频繁出现重复值的常见场景下,列式存储就是一个非常有意义的选择要素。此外,数据压缩也是存储上需要考虑和权衡的重要因素,不同的数据压缩算法,对数据的创建、读取、空间占用都有很大的影响。常见的选择是GZIP/LZO等各方面指标相对较为均衡的算法。 此外,即使存储成本不是主要因素,光为了性能往往也需要启用压缩存储。 这是由于分布式存储下数据分布较为散乱,因此集群瓶颈往往是在网络而不是CPU上。
3. 表连接
数据仓库建设中,关联查询能力是非常重要的一环。是否支持内连接、左连接等多种表连接方式,大表查询性能是否足够快是非常重要的考虑因素,这和是否合理选择了表的存储格式也有非常大的关联。一个不好的引擎,可能在表连接时会产生大量的磁盘IO和节点间网络IO。实际工作中有不少引擎对于等效的SQL在做表连接时的连接次序都可能产生非常大的性能差距。
4. UDF能力
在Hadoop生态中,我们往往还需要或多或少一些处理非结构化的数据,这个能力和前文所提到的兼容性问题,构成了我们对UDF能力的诉求,是否支持UDF开发、开发成本如何(只能Java/C++,还是支持Python脚本等?)、UDF代码的部署便利程度都是重要的考虑因素。
5. 资源调度
一个成熟的数据仓库会有众多的用户,大量的数据处理任务同时并发执行,不同类型的任务或查询谁能获得更多的资源,需要合理的管控策略和机制。 交互式查询如何分配更多资源保证优先处理,长时间运行的批处理任务如何降低优先级确保不影响别人但又能在集群空闲时最大化的利用资源,这些问题都是对引擎的巨大挑战。
三、OLAP、ROLAP、MOLAP及HOLAP
相对我们日常业务所使用的具有完备ACID特征的OLTP(Online Transaction Processing)数据库而言,分析上我们更多使用的是OLAP(Online Analysis Processing),其主要差异在于基本只读,数据更多是批量的导入和处理转换,几乎没有修改。此外,分析类的需求在聚合及筛选上的的要求要更高,而主键定位类的需求相对反而不太重要。其中主要有三大类,分别是ROLAP(Relational OLAP)、MOLAP(Multi-Dimensional OLAP)和HOLAP(Hybird OLAP)。
ROLAP一般是直接把线上数据原样或抽样导入数仓后,无需转换处理直接在原始数据上进行分析,对于不同维度的数据筛选需要通过相关字段关联到维度表上通过关系连接进行处理。ROLAP的性能通常较为低下。
而MOLAP则是通过进行预处理,把源数据转换成由维度ID及指标数据组成的几乎只有数字值的多维事实表,这样会产生额外的存储空间占用,但由于已经完成了预计算(相同维度ID组合的数据已累计汇总至指标字段中),其查询性能相对ROLAP会非常高。但同时由于丢失了源数据上的细节,当分析需求的维度并未包含在原始数据中时需求就难以得到满足了。
HOLAP则介于两者之间,两份数据都存,并根据查询条件在两种不同的存储间切换。其空间占用最大,性能则在两者之间。
如前文所述,其实在过往单机BI的时代,几乎80%以上的数据仓库都是经过维度建模的MOLAP Cube。而在大数据时代,则绝大部分的引擎都只考虑了ROLAP的支持,只有少数项目如Apache Kylin是天生为MOLAP而设计的。这是由于数据立方体(Cube)的建模过程是去细节化的,而在大数据时代,我们对源数据的分析是在海量这个与以往不同的前提下产生,无论是分析还是挖掘工作的探索都有赖于对源数据的精细理解。甚至如Google的BigQuery这样的项目,则更加激进的采用了全JSON结构存储,并且无需指定Schema约束。但事实上,企业内部的分析需求,至少80%以上是脱不出事先设计好的范畴的(即分析用到的所有维度基本都在框定范围内),所以很多数据仓库的实践目前仍建立在OLAP Cube的基础上。大数据分析系统的选型,必须综合考虑业务特征,选择合适的工作模式及SQL引擎,方能高效支持好业务。
四、不得不说的Apache Hive
做为Apache Hadoop中的重要成员之一,Hive是目前大数据处理的最重要产品之一,在离线数据批量处理领域几乎没有替代解决方案。由于实时计算几乎都离不开离线计算的预处理,这也就意味着几乎每个数据平台都或多或少的在使用着Hive。甚至很多号称性能比Hive强很多的引擎,也某种程度上依赖于Hive的部分实现。
Hive把数据文件存在HDFS上,并提供了跟MySQL大部分语法相似的Hive-QL,也用了相似的database/table/view结构来组织数据集。它提供了命令行客户端、JDBC驱动及Thrift API三大模式来使用SQL访问数据。这些SQL语句会被Hive翻译为MapReduce任务提交到Hadoop集群上执行并返回结果。
Hive最大的缺点之一是其调度依赖于MapReduce,而在交互式查询上这是一个致命伤——MapReduce即使超级简单的小任务完成整个调度都需要至少10几秒以上,而我们对交互式查询的延迟容忍可能是无法超过5-15秒的。现有的解决方案之一是Hive on Spark,借助RDD模式下内存优先的新计算模式,Hive可以跑得更快更好,可惜却仍然没有办法达到交互式查询的层级。
在实际应用Hive的过程中,有几个大的问题需要注意去调优或控制:数据倾斜、MapReduce的Task策略、启用严格模式、资源竞争策略。其中的数据倾斜问题,在设计表结构及规划分区字段时就必须做好,否则线上环境调整的成本会非常高昂;而严格模式则是制止分析师做常见蠢事的必要机制,启用后where中不包含分区字段的查询将直接被禁止执行,有效避免了一些传统数据库操作时的陋习,比如在一个巨型表上执行select count(1) from table。
Hive有非常完备的自定义函数(UDF,User Defined Function)开发能力,并且能够在单一机器加载UDF后自动推送到所有机器上执行。其中有三种模式:UDF、UDFA、UDTF,分别支持一进一出,多进一出、一进多出等三种数据处理模式,后两种可用于做聚合计算及非结构化数据处理等。在ETL处理中我们往往需要的一些业务规则转换也都可以通过UDF来实现支持,最终使得无需分析师无需编程即可使用SQL直接处理数据。
五、Impala、Spark SQL等
由于Hive在交互式查询能力上的不足,Hadoop生态中衍生了各种不同的SQL引擎。包括Impala、Spark SQL、Presto等。而交互式查询是BI系统中非常重要的关键能力,我们对SQL on Hadoop的不同选择也往往是体现在这一环上。
Impala是Cloudera推出的产品,其特色是使用C++开发(SQL解析部分仍为Java),改变了SQL分布式执行的模式。通过默认使用列式存储、消除中间结果的磁盘IO、使用MPP思想基于自己的服务进程调度而不是用MapReduce、针对性的硬件优化、优先本地计算调度等诸多策略,其相比Hive而言性能有明显的改进。技术的改进也使得Impala在多租户、多并发的业务场景下表现稳定,因此特别适合用于构建实时查询分析平台。同时它也是对ANSI SQL支持最好的SQL on Hadoop引擎之一,基本达到了SQL99的标准,这对有很多历史积累的老系统而言是个重大利好消息。
Spark SQL主要的推动者是Databricks。其前身Shark把Hive的物理执行计划使用Spark计算引擎去执行。但由于Hive社区并没有把物理执行计划到执行引擎这个步骤抽象出公共API,Shark要自己维护一个Hive的分支,导致其总是比Hive晚一两个版本,所以后来社区就停止了Shark的开发转向Spark SQL。而通过把SQL解析成RDD的transformation和action,并利用catalyst可以自由、灵活的选择最优执行方案,性能比起Hive有非常大的提升。Spark SQL同时也有HiveContext来兼容Hive,包括HiveQL、Hive Metastore、Hive的UDF等,也一样支持JDBC连接。 然而生产上应用Spark SQL最大的两个问题是它的SQL兼容性略差以及还不能很好的分配资源和调度任务,后者会使得批数据处理时往往出现资源不充分利用最终导致计算时间超出可控范围的情况。 因此,由于数据平台的负责人们都承担不起数据集群一整晚利用率不高,但第二天早上老板要看的数据却没出来的恶果,所以尽管Spark SQL性能比Hive好很多,但还暂时无法撼动Hive在批处理领域的地位。
Presto则是Facebook推出的开源引擎,同样不使用MapReduce,并且尽可能在内存中倒腾而不写磁盘,用于ROLAP场景下性能非常出色。而Apache Drill则是对Google Dremel(BigQuery就是基于Dremel)的复刻,对于数据内容无需事先定义Schema,访问时指定位置信息即可,甚至连SQL语句中的表名都可以直接替换为HDFS上的文件名。这使得Drill能非常好的支持一些业务模式变动较快的场景,对于缺乏事前规划能力能力的技术型团队也有一定的价值。
六、Apache Kylin
Apache Kylin 是一个开源的分布式的 OLAP 分析引擎,由 eBay 开发并开源,基于 Hadoop 提供 SQL 接口和 OLAP 接口,目前已经是Apache的顶级项目之一。Kylin的设计初衷是减少Hadoop在10亿及百亿规模以上数据级别的情况下的查询延迟,其底层数据存储基于HBase,具有较强的可伸缩性。Kylin能够支持在秒级别延迟的情况下同Hadoop进行交互式查询、支持多维联机分析处理数据仓库(MOLAP Cube)、支持JDBC及ODBC接口。Kylin和现代BI工具如Tableau、Excel、PowerBI等都可以无缝连接,最新的0.7.x中实现了流式近实时Cube计算,可以很好的支持实时数据多维分析等各种场景。
在Kylin之前,也有其他基于HBase实现高性能SQL的选择方案,一些企业内部也会基于HBase去做一些预计算及缓存加速的策略,然而有两个难点在Kylin上取得了突破:一是引入Calcite解决了SQL到MOLAP Cube的映射问题,使得MOLAP Cube的高性能优势得以充分发挥却不再受限于只能使用专业BI工具,二是基于HBase做了所有字段组合缺省情况下的数据预计算,这让HBase scan的次数大幅下降。这两个难点的突破使得Kylin在通用性和性能上有了突破性的进展。除此之外,Kylin利用MapReduce来导入数据、HBase存储内容启用压缩等等方面也都很好的改进了ETL、数据调取过程中的性能。
在笔者过往经历的实际项目中,有很多Case是在百亿级源数据上,基于Kylin实现了建模后数据2秒内获得查询结果。这使得带数据联动能力的仪表盘、关联报表在体验上具备了可行性,而不再像以往需要针对这些报表生成缓存或加速表。目前包括中国移动、百度、网易、OPPO、唯品会等公司都陆续在生产上使用Kylin替代以往的引擎来提供高性能数据分析查询服务。