前言
大数据平台中Hadoop的分布式文件系统(HDFS)之上形成了一种极具特色的技术群体,那就是SQL查询引擎。这就包括了Hive、Impala、Presto、Spark SQL等;在分布式数据库HBase也具有Impala、phoenix这样的SQL外观,可以通过SQL与HBase交互;另外分布式关系模型数据库(NewSQL),例如:cockroachdb的sql layer、TiDB的tidb模块等在自身架构体系中不仅是查询引擎,也是SQL插入、更新和删除的SQL执行引擎。
作为海量数据计算存储平台,特别容易提出的一个概念就是NoSQL,可是从上面的发展看,不像No SQL么,感觉像是:这个世界没法NoSQL,以前我有篇文章,深入分析了关系模型的真正价值。
我把这篇文章中的一小段摘录出来,回顾一下关系模型是什么:
关系型模型之父Edgar F. Codd,在1970年发表了《大型共享数据库数据的关系模型》 原始的关系模型:
(1)结构(structure)
(2)完整性(integrity)
(3)操作(manipulation)
原始理论具体到实现再翻译成我们好理解的描述:
结构:就是我们经常要先对数据库预先定义的表名和字段(名称、类型)
完整性:就是表的主键不能为空,表与表之间的主/外键关联必须保证是完整的,外键一定是能找到主键的。
操作:那就是SQL表达式啦,SQL的子查询就是典型的闭包(Closure),可以形成嵌套表达式。
文章链接:
HDFS/HBase技术报告·分布式数据库设计架构的全面解析https://blog.csdn.net/u011457868/article/details/115896789?spm=1001.2014.3001.5502
https://blog.csdn.net/u011457868/article/details/115896789?spm=1001.2014.3001.5502
因此SQL只是关系模型的一种具体实现。我们只是在大数据领域需要替换关系型数据库的存储逻辑,使得数据库更分布式化,更容易实现存储架构的扩展。因为这是单机存储与计算到了天花板后,必须横向扩展的硬需求,但这也并不是说关系模型过时了!
实际上Hadoo的HDFS、HBase提供的Java接口也好,Spark提供的Java、Scala、Python接口也好,这都需要使用者至少懂一门编程语言才行,Python虽然易学易懂,但也只限于Spark的原生支持,而且Python好歹也是个面向对象编程语言正规军,至少也得让使用者明白什么是面向对象吧?那对于科学家、科研人员、数据分析师、数据运维工程师,学生,基本都是大数据的主要使用者,编程接口的方式对于他们就极为不友好了。这时候再看SQL,老少皆宜,不分阶层,大家好,才是真的好!对吧。
SQL查询引擎架构 Hive-ON-Hadoop
在SQL-ON-Hadoop架构中,Hive依然是成熟度最高SQL引擎,有些文章也把它叫做数仓工具,总之Hive已经从SQL解释器发展成为独立性很高的SQL引擎层,仍依赖Hadoop HDFS存储层,但摆脱了执行层Mapreduce计算引擎的束缚,形成可替换的计算引擎层,可以将MapReduce引擎替换成Tez、Spark。
为什么要替换MapReduce呢?就是因为MapReduce对数据写进写出的方式太过生硬,直接用磁盘作为计算中间过程的数据存储,导致分布式并行处理的性能很差。并行计算在磁盘上进行,其实也是一把双刃剑,不能一味地认为Mapreduce性能差,对于超大规模数据量的批处理,内存是放不下的,所有基于内存模型的并行处理引擎,都会因为内存不够,将需要处理的数据吐到磁盘上,这反而加重了I/O的吞吐,导致不佳的性能。但是在足够的内存条件下,中小数据量的批处理上,基于内存的计算引擎会非常快地给出结果,所以这是必然趋势。
Tez的性能比起MR很快,我在实际环境中也测试过,不仅是内存计算带来的性能提升,它在执行数据处理前比MR引擎有自动的优化策略,以后会成为Hive的默认执行引擎,Hive 执行引擎也可以替换成Spark,即Hive-on-Spark。但是我认为在新项目中直接就是Spark-SQL就够了,没必要再搞个Hive,因为底层都是Spark。Hive-on-Spark的主要目标是那些已经部署实施的Hive系统,通过Spark对Mr的替换,极大提升查询分析速度。
如下图所示:Hive依赖Hadoop,左边是Hive SQL查询过程,右边是Hadooop协作过程。
第一阶段:Hive接收UI的SQL查询,进入Driver和Compiler进行SQL解析并生成执行计划。并发送执行计划给Hive执行引擎(Execution engine)。
第二阶段:主要是在Hive执行引擎将执行计划形成阶段DAG,调度进Hadoop。
第三阶段:Hadoop的MR执行引擎中实现Map和Reduce的任务执行,并且将过程产生的临时数据保留在HDFS。
第四阶段:最终计算结果由Hive执行引擎从HDFS中取出,并发送结果到Driver,再由Driver返回结果给UI。
Impala
曾经Google公开了两篇论文,一篇涉及F1支持容错的分布式SQL数据库,另一篇涉及名为Dremel的可扩展的交互式即时查询引擎,在前者的指引下,出现了像PingCAP公司的开源分布式关系数据库TiDB,可以看这篇文章:每日一答:CAP 理论常被解释为“三选二”定律,是否是一种误解?对Spanner/F1容错设计的简描述。在后者的启发下,2012年发布了Hadoop之上的开源、低延迟SQL查询引擎Impala。
Impala有几个特点:
支持HDFS、HBase作为数据源。
复用HiveSQL方言,使用Hive元数据服务,因此Hive表一次定义,Hive、Impala皆可使用。
Impala的查询延迟比Hive+mapreduce要低得多。
Impala架构是无共享模式,因此支持系统级容错性和强大的可扩张性。
Impala也是基于内存的计算引擎,多个处理阶段的中间过程,数据会保存在内存中,2.0版本之后支持大数据量内存与磁盘I/O数据交换。
Impala是以后台服务的形成长期运行,一般推荐Impala服务在HDFS的DataNode节点上运行,保证计算尽量在本地读取数据,减少网络传输。
Impala是C++语言实现,可以充分利用机器内存,单个Impala进程不用像Java程序那样,受限于JVM垃圾回收机制的延迟影响。
Impala具有一项高性能的查询黑科技:底层虚拟机(Low Level Virtual Machine, LLVM)编译查询,可以将查询方法编译成优化的机器码,提高CPU对代码的执行性能。
Impala架构如下图所示:impala后台服务(Impalad)尽量在数据节点上运行(HDFS的Datanode,DD、HBase);每个Impalad包含了查询规划器(Query Planner)、查询规划器(Query Coordinator)和查询执行器(Query Exec Engine)
第一步查询规划器用于解析由JDBC、ODBC、impala-shell或者Thrift等客户端请求的SQL语句,产生执行计划规划器。
第二步连接到当前客户端的查询规划器将执行计划将不同执行部分分配到各个Impalad中。
第三部各个Impalad将获取到的部分执行计划,并交给查询执行引擎,访问本地HDFS DD或HBase数据,实现大规模并行处理(MPP),完成查询处理后返回结果,由原计划分发点实现汇聚与回应客户端结果。
Impala VS Presto
架构对比
2013年Facebook开源了Presto,支持标准ANSI SQL。Presto的特点特别适合与Impala做一个比较。
首先如果把Impala比喻成一位用情专一的君子,那么Presto就是处处留情的公子。Impala用于查询的主要基础存储系统就是Hadoop HDFS与HBase,我们再看看Presto都设计了多少种存储连接:
Accumulo Connector |基于HDFS的K-V存储
BigQuery Connector |Google的大数据查询引擎云服务
Cassandra Connector
Druid Connector | 实时分析型数据库
Elasticsearch Connector
Hive Connector
Kafka Connector
Kudu Connector |基于HDFS的列式快速分析存储系统
Local File Connector
Memory Connector |直接在内存中建立数据表进行操作
MongoDB Connector
MySQL Connector
Oracle Connector
PostgreSQL Connector
Redis Connector
SQL Server Connector
Thrift Connector |远程过程调用RPC框架
......
但是有一点我感到很不解,Presto都支持那么多存储系统和框架了,而且最早的设计初衷就是为了Hadoop HDFS的高效查询,既然Impala都是直接支持HBase了,为什么Presto不支持HBase呢?有了解情况的朋友还希望给予指点。如下图所示:
其次,Impala更像是一个传统的大规模并行处理(MPP)数仓工具,每个进程都是独立接受客户端请求的,客户端连接到哪个服务,该服务就作为分布式调度者,大家可以看看上篇中的Impala架构图。
但是Presto就是一个标准的主从架构了,客户端要先与主节点打交道,主节点再将SQL解释后的执行计划分配到不同的work节点去执行,并由一个work节点作为结果汇聚返回节点,如下图所示:
最后,我们再看看Impala和Presto在SQL解释、优化和调度之间的对比。如下图所示:左边是Impala架构,右边是Presto架构。
上篇已经描述过Impala主要是任意连接客户端的Impalad后台服务,接收SQL请求,解析SQL并制定执行计划后,由当前节点在Query Coordinator阶段对执行计划各个部分进行调度分配,再由集群其他Impalad后台服务根据调度进入Query Exec Engine阶段实现并行处理。最终结果还是汇聚到调度节点反馈客户端。
Presto获取用户提交的SQL查询,首先进行SQL语句解析( Parser),接着形成逻辑计划相关对象(Planner)放入线程池,最后由调度器(Scheduler)对逻辑计划生成的SubPlan提交到多个Work节点上执行。