今天,我们很高兴宣布Apache Drill 1.4现已在MapR发行版中可用。 钻1.4是MAPR生产就绪和支持的版本,可以从下载这里 ,找到1.4版本说明这里 。
Drill 1.4以其高度灵活和可扩展的体系结构为基础,带来了多种新功能以及对查询性能的增强,使其成为Drill社区非常重要的里程碑。
这是Drill 1.4中可用的关键功能/增强功能的列表。
- 通过更快的限制0查询改善Tableau体验
- Hive模式/表上的元数据(INFORMATION_SCHEMA)查询加速
- 通过增强的分区修剪来优化查询计划和执行
- 高效地缓存Parquet元数据,加快了对大量文件的查询
- 改进的窗口功能,资源使用率和性能
- 表功能
- 改进的CSV标头解析
- 新的和改进的MapR Drill JDBC驱动程序
在此博客文章中,我想专门简要概述一下最近的一些性能增强功能,即分区修剪和Parquet元数据缓存,这将使您能够在Drill部署中实现较低的延迟响应时间。 元数据缓存是Drill 1.2中新增的一项功能,自Drill 1.0以来就存在分区修剪功能,但在1.4版本中,这两项功能效率更高,并且涵盖了广泛的用例。
让我从一些背景开始。 Drill旨在在包含多种数据类型和数据源的大规模数据集上实现交互性能。 任何查询引擎中的性能均由两部分组成:
- 解析查询和创建最佳查询计划所花费的时间(又名查询计划时间)。
- 通过从底层存储系统中检索和处理数据来跨集群中的各个节点执行生成的查询计划所花费的时间(又称为查询执行时间)。
以下是在每个阶段中使Drill获得交互性能的一些核心Drill体系结构元素和技术的列表。 如您所见,分区修剪和元数据缓存都是作为查询计划一部分应用的优化技术的示例。
分区修剪
像Hadoop这样的大数据系统中的数据集大小可能是巨大的,范围从TB到PB。 在某些情况下,数据集可能从很小的开始,但是客户选择Hadoop是因为他们希望数据量显着且快速地增长。 分区修剪使查询引擎能够确定和检索所需的最小数据集来回答给定查询。 读取小数据意味着IO上的周期更少,而CPU上实际处理数据的周期也更少。 这是应用于传统DBMS / MPP系统中以实现性能的标准技术,但是由于大数据量,在大数据环境中变得更加重要。 为了利用分区修剪作为查询的一部分,需要根据您希望从用户那里获得的查询模式对数据进行适当地组织和分区。
组织数据可以在摄取时完成,或者随后通过使用各种Hadoop生态系统工具(例如Flume,Hive,Pig)或作为处理步骤完成,对于MapR,可以通过NFS直接摄取。 Drill支持使用各种类型的存储插件进行分区修剪。 在基于文件的目录结构查询文件系统时以及在查询Hive表时使用Hive Metastore表分区信息时,将应用分区修剪。 Drill本身提供了创建分区数据的功能,这是CREATE TABLE AS语法的一部分。
这是使用Drill SQL语法对数据进行分区的示例。 该语句将示例Yelp业务JSON数据集(可以从Yelp下载)转换为Parquet格式。 作为转换的一部分,数据还会根据州,城市和星号三列进行分区。
0: jdbc:drill:zk=local> create table dfs.tmp.businessparquet partition by (state,city,stars) as select state, city, stars, business_id, full_address, hours,name, review_count from `business.json`;
上面语句的输出是在与指定工作空间相对应的目录中生成的Parquet数据。 在这种情况下,dfs.tmp工作区指向文件系统上的/ tmp位置,并且生成的目录为/ tmp / businessparquet,这是SQL子句中指定的表名。
让我们获取CTAS命令生成的文件数。
NRentachintala-MAC:businessparquet nrentachintala$ cd /tmp/businessparquet/
NRentachintala-MAC:businessparquet nrentachintala$ ls -l |wc -l
652
请注意,Drill CTAS命令生成的文件数量可以在Drill中使用各种参数进行调整。 但是,默认值匹配CTAS中指定的分区键列将具有的不同组合的数量。 例如,以下SQL语句为您提供了分区键列的不同组合数。
0: jdbc:drill:zk=local> select count(*) from (select distinct state, city, stars from dfs.yelp.`business.json`) ;
+---------+
| EXPR$0 |
+---------+
| 652 |
+---------+
现在已经对Parquet数据进行了分区,使用分区列(州,城市,星号)上的过滤器进行的查询可以利用分区修剪优化。 仅从磁盘读取相关数据,其余的分区在计划时修剪掉。
您可以通过在查询上运行EXPLAIN PLAN命令或从Drill Web UI(可以从Drillbit节点的8047端口启动)中查看配置文件,来轻松检查是否对给定查询应用了分区修剪。
让我们进行几个示例查询,看看是否使用Web UI进行了分区修剪。
这是一个在两个分区列(州和城市)上都带有过滤器的查询。
0: jdbc:drill:zk=local> select name, city, stars from dfs.tmp.businessparquet where state='AZ' and city = 'Fountain Hills' limit 5;
+-----------------------------------------------+-----------------+--------+
| name | city | stars |
+-----------------------------------------------+-----------------+--------+
| Fry's Food & Drug Stores | Fountain Hills | 2.0 |
| Burger King | Fountain Hills | 2.0 |
| Francis & Sons Car Wash | Fountain Hills | 2.0 |
| Kimmies | Fountain Hills | 2.0 |
| Le Baron Cleaners At Basha's Shopping Center | Fountain Hills | 3.5 |
+-----------------------------------------------+-----------------+--------+
5 rows selected (0.308 seconds)
在Web UI中,此查询的物理查询计划如下所示。 注意配置文件中突出显示的“ numFiles”值。 这表示从磁盘上读取了多少个文件以服务查询。 在这种情况下,将读取652个文件中的9个文件,因为查询对作为分区键的州和城市列都应用了过滤器,并修剪了剩余的数据分区。 检查读取的文件数是确保是否应用分区的一种简单方法。
00-00 Screen : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {129.5 rows, 501.5 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 731
00-01 Project(name=[$0], city=[$1], stars=[$2]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {129.0 rows, 501.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 730
00-02 SelectionVectorRemover : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {129.0 rows, 501.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 729
00-03 Limit(fetch=[5]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {124.0 rows, 496.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 728
00-04 Limit(fetch=[5]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {119.0 rows, 476.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 727
00-05 Project(name=[$2], city=[$1], stars=[$3]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 114.0, cumulative cost = {114.0 rows, 456.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 726
00-06 Project(state=[$1], city=[$2], name=[$0], stars=[$3]) : rowType = RecordType(ANY state, ANY city, ANY name, ANY stars): rowcount = 114.0, cumulative cost = {114.0 rows, 456.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 725
00-07 Scan(groupscan=[ParquetGroupScan [entries=[ReadEntryWithPath [path=/tmp/businessparquet/0_0_111.parquet], ReadEntryWithPath [path=/tmp/businessparquet/0_0_114.parquet], ReadEntryWithPath [path=/tmp/businessparquet/0_0_115.parquet], ReadEntryWithPath [path=/tmp/businessparquet/0_0_110.parquet], ReadEntryWithPath [path=/tmp/businessparquet/0_0_109.parquet], ReadEntryWithPath [path=/tmp/businessparquet/0_0_113.parquet], ReadEntryWithPath [path=/tmp/businessparquet/0_0_116.parquet], ReadEntryWithPath [path=/tmp/businessparquet/0_0_117.parquet], ReadEntryWithPath [path=/tmp/businessparquet/0_0_112.parquet]], selectionRoot=file:/tmp/businessparquet, numFiles=9, usedMetadataFile=false, columns=[`state`, `city`, `name`, `stars`]]]) : rowType = RecordType(ANY name, ANY state, ANY city, ANY stars): rowcount = 114.0, cumulative cost = {114.0 rows, 456.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 724
现在,通过添加另一个带有stars列的过滤器来扩展上述查询,该过滤器也是一个分区键。
0: jdbc:drill:zk=local> select name, city, stars from dfs.tmp.businessparquet where state='AZ' and city = 'Fountain Hills' and stars= '3.5' limit 5;
+-----------------------------------------------+-----------------+--------+
| name | city | stars |
+-----------------------------------------------+-----------------+--------+
| Le Baron Cleaners At Basha's Shopping Center | Fountain Hills | 3.5 |
| Euro Pizza Cafe | Fountain Hills | 3.5 |
| Deluxe Nail & Spa | Fountain Hills | 3.5 |
| Ha Ha China | Fountain Hills | 3.5 |
| Pony Express | Fountain Hills | 3.5 |
+-----------------------------------------------+-----------------+--------+
5 rows selected (0.342 seconds)
请注意,此查询的物理计划如下所示,“ numFiles”仅显示为1。因此,Drill必须读取652个文件中的1个才能回答查询。 查询中基于分区的过滤器越多,查询就可以指向数据的特定子集。 这可能会导致巨大的性能改进。 但是请注意,您的查询可能非常复杂,在这种情况下,从分区修剪中获得的性能优势可能无法与查询的处理成本相提并论。 但是,在大多数简单和中等查询中,这将是很大的帮助。 同样,利用分区修剪的最重要方面是弄清楚常见的查询模式并相应地对数据进行分区。 花一些时间来调整您的部署。
00-00 Screen : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {40.5 rows, 145.5 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1005
00-01 Project(name=[$0], city=[$1], stars=[$2]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {40.0 rows, 145.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1004
00-02 SelectionVectorRemover : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {40.0 rows, 145.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1003
00-03 Limit(fetch=[5]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {35.0 rows, 140.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1002
00-04 Project(name=[$3], city=[$1], stars=[$2]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 30.0, cumulative cost = {30.0 rows, 120.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1001
00-05 Project(state=[$1], city=[$2], stars=[$3], name=[$0]) : rowType = RecordType(ANY state, ANY city, ANY stars, ANY name): rowcount = 30.0, cumulative cost = {30.0 rows, 120.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1000
00-06 Scan(groupscan=[ParquetGroupScan [entries=[ReadEntryWithPath [path=/tmp/businessparquet/0_0_114.parquet]], selectionRoot=file:/tmp/businessparquet, numFiles=1, usedMetadataFile=false, columns=[`state`, `city`, `stars`, `name`]]]) : rowType = RecordType(ANY name, ANY state, ANY city, ANY stars): rowcount = 30.0, cumulative cost = {30.0 rows, 120.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 999
Parquet元数据缓存
Hadoop部署的另一个共同特征是文件系统上的文件数量。 我们已经看到客户使用Drill查询成千上万的文件,用于报告和ETL用例。 Drill的与众不同的功能之一是它能够处理自描述数据格式(例如Parquet)并即时发现模式。 Parquet将有关数据的元数据存储为文件页脚的一部分,并且包含诸如列名,数据类型,可空性和其他列特性之类的信息,以及围绕数据布局的参数(例如行组大小)。 Drill将这些信息作为计划时间的一部分。 尽管Drill具有在查询时发现此元数据的能力,但是对于存在许多文件的用例来说,这可能是一项昂贵的操作。 从Drill 1.2开始,我们引入了在Drill中缓存Parquet元数据的功能。 缓存元数据后,可以根据需要刷新它,具体取决于数据集在环境中的更改频率。
以下是使用缓存元数据的命令。 该命令可用于文件夹或单个文件。
0: jdbc:drill:zk=local> REFRESH TABLE METADATA dfs.tmp.BusinessParquet;
+-------+-----------------------------------------------------------+
| ok | summary |
+-------+-----------------------------------------------------------+
| true | Successfully updated metadata for table BusinessParquet. |
+-------+-----------------------------------------------------------+
1 row selected (0.455 seconds)
Web UI或“解释计划”命令中的查询配置文件显示了给定查询是否利用了元数据缓存。
0: jdbc:drill:zk=local> select name, city, stars from dfs.tmp.businessparquet where state='AZ' and city = 'Fountain Hills' and stars= '3.5' limit 5;
请注意,以下配置文件中突出显示的“ usedMetadataCacheFile = true”表示此命令利用了元数据缓存。
00-00 Screen : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {40.5 rows, 145.5 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1279
00-01 Project(name=[$0], city=[$1], stars=[$2]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {40.0 rows, 145.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1278
00-02 SelectionVectorRemover : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {40.0 rows, 145.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1277
00-03 Limit(fetch=[5]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 5.0, cumulative cost = {35.0 rows, 140.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1276
00-04 Project(name=[$3], city=[$1], stars=[$2]) : rowType = RecordType(ANY name, ANY city, ANY stars): rowcount = 30.0, cumulative cost = {30.0 rows, 120.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1275
00-05 Project(state=[$1], city=[$2], stars=[$3], name=[$0]) : rowType = RecordType(ANY state, ANY city, ANY stars, ANY name): rowcount = 30.0, cumulative cost = {30.0 rows, 120.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1274
00-06 Scan(groupscan=[ParquetGroupScan [entries=[ReadEntryWithPath [path=/tmp/BusinessParquet/0_0_114.parquet]], selectionRoot=/tmp/BusinessParquet, numFiles=1, usedMetadataFile=true, columns=[`state`, `city`, `stars`, `name`]]]) : rowType = RecordType(ANY name, ANY state, ANY city, ANY stars): rowcount = 30.0, cumulative cost = {30.0 rows, 120.0 cpu, 0.0 io, 0.0 network, 0.0 memory}, id = 1273
分区修剪和元数据缓存的结合可以为各种查询带来巨大的性能提升,尤其是在临时查询/报告用例的情况下。 我们将在随后的博客文章中提供有关这些优化以及其他各种Drill性能功能和最佳实践的更深入的信息。
有关Drill 1.4功能的更多详细信息和文档,请参见MapR文档和Drill文档 。 祝贺Drill社区有了另一个重要的里程碑。 祝您钻Kong愉快!
这是您可以开始使用Drill的多种方法:
- 在10分钟内在笔记本电脑上开始使用Drill
- 将Drill与Hadoop结合使用-MapR沙箱和教程
- 尝试使用Amazon Web Services进行钻取
- 将Drill下载到您的MapR集群
- 按需训练
- 详细的分步教程
翻译自: https://www.javacodegeeks.com/2016/01/brief-overview-performance-enhancements-apache-drill-1-4.html