Impala性能优化要点:
1. 为数据存储选择合适的文件格式(如:Parquet)
通常对于大数据量来说,Parquet文件格式是最佳的
2. 防止入库时产生大量的小文件(insert ... values
会产生大量小文件,应该避免使用)
在impala外生成数据时,最好是text格式或者Avro,这样你就可以逐行的构建文件,到了impala之后,再通过简单的insert ... select
语句将其转换为Parquet格式
3. 根据实际的数据量大小选择合适的分区粒度
合适的分区策略可以对数据进行物理拆分,在查询的时候就可以忽略掉无用数据,提高查询效率,通常建议分区数量在3万以下(太多的分区也会造成元数据管理的性能下降)
4. 为分区key选择最小的整数类型
虽然使用string类型也可以作为分区key,因为分区key最后都是作为HDFS目录使用,但是使用最小的整数类型作为分区key可以降低内存消耗
5. 选择合适的Parquet块大小
默认情况下,Impala的insert ... select
语句创建的Parquet文件都是每个分区256M(在2.0之后改为1G了),通过Impala写入的Parquet文件只有一个块,因而只能被一个机器当作一个单元进行处理。如果在你的Parquet表中只有一个或者几个分区,或者一个查询只能访问一个分区,那么你的性能会非常慢,因为没有足够的数据来利用Impala并发分布式查询的优势。
6. 在追求性能或者大数据量查询的时候,要先获取所需要的表的统计指标(如:执行compute stats
)
7. 减少传输到client端的数据量
我们可以通过如下方式来降低到客户端的数据量:
- 聚合(如
count、sum、max
等) - 过滤(如
WHERE
) LIMIT
- 结果集禁止使用美化格式进行展示(在通过impala-shell展示结果时,添加这些可选参数:
-B、 --output_delimiter
)
8. 确认查询是以高效的逻辑方式进行规划
在执行之前使用EXPLAIN来查看逻辑规划,分析执行逻辑
9. 使用合适的操作系统设置
Impala Join性能优化
Impala join查询最简单的优化手段就是通过使用compute stats
来收集join中每张表的统计信息,然后由Impala根据表的大小、列的唯一值数目等来自动优化查询。为了更加精确地获取每张表的统计信息,每次表的数据变更时(如执行insert
、load data
、add partition
、或drop partition
等)都要重新执行一遍compute stats
。
如果join查询中表的统计信息不全或者Impala选择的join顺序不是最优时,你可以在select [distinct 、all]
之后指定straight_join
来覆盖掉impala的join顺序如:
select straight_join x
from medium join small join (select * from big where c1 < 10) as big
where medium.id = small.id
and small.id = big.id;
select distinct straight_join x
from medium join small join (select * from big where c1 < 10) as big
where medium.id = small.id
and small.id = big.id;
这样Impala就会使用查询语句中表的顺序来指导join的处理。
当使用STRAIGHT_JOIn
技术时,你必须手动指定join查询中表的顺序而不是依赖于Impala优化器。Impala优化器使用特殊的手段来估算join中每个阶段的结果集大小,而对于手动指定顺序来说,可以根据如下方式开始,然后再手动调节来达到最优:
- 首先指定最大的表,此表一般保存于磁盘中
- 指定最小的表,第二张表、第三张表等等之后的表都是通过网络传输的,你需要对这些结果集进行裁剪处理以降低传输数据量
- 指定次小表,再到次次小表等
例如:如果你的表的大小如下:BIG
、MEDIUM
、SMALL
和TINY
,那么你的顺序应该如此:BIG join TINY join SMALL join MEDIUM
。
Impala查询优化器根据表的绝对或者相对大小来选择不同技术来执行join查询。默认情况下是 broadcast join
,右边的表都是小于左边的表,右边的表的内容会被分发到其他的查询节点中。
另一种技术就是partitioned join
,这种技术更加适用于大小差不多大的大表join,使用这种方式的话,每张表中的分区都会把数据分发到其他节点中,这样就可以这些数据子集就可以并发处理了。 broadcast
或者partition join
的选择是根据compute stats
采集到的可用统计指标来衡量的。对于指定查询语句,可以通过执行EXPLAIN
就可以查看选用的是哪个join策略。
统计指标不可用时join如何查询
当join中表或者列的统计指标不可用时,Impala将无统计指标的表认为统计指标都为0,这些表都将作为右表处理。
通过EXPLAIN Plans和Query Profiles来了解Impala查询性能
想要了解Impala查询的高性能注意事项,可以阅读查询的EXPLAIN
输出,你可以获取EXPLAIN
的执行计划,而无须真正的执行query。
想要查看一个查询的物理性能特性的概览,可以在执行查询之后立马在impala-shell
中执行SUMMARY
命令,输出的信息中将展示哪个阶段耗时最多,以及每一阶段估算的内存消耗、行数与实际的差异。
想要了解查询的详细性能特征,可以在执行查询之后立马在impala-shell
中执行PROFILE
命令,这些底层的信息包括内存、CPU、I/O以及网络消耗的详细信息,因此只能在一个真实的查询之后才可用。
使用EXPLAIN进行性能优化
EXPLAIN
语句概述了查询将执行的逻辑步骤,例如如何在节点间分配工作以及中间结果如何合并为最终结果, 这些你都可以在查询真正执行之前获得,你可以使用这些信息来检查查询是否会以某种非高效的方式执行。
[9-24-143-25:21000] > explain select ds,count(*) from t_ed_xxxx_newuser_read_feature_n group by ds order by ds;
Connection lost, reconnecting...
Query: explain select ds,count(*) from t_ed_xxxx_newuser_read_feature_n group by ds order by ds
+----------------------------------------------------------------------------------------------+
| Explain String |
+----------------------------------------------------------------------------------------------+
| Max Per-Host Resource Reservation: Memory=9.94MB |
| Per-Host Resource Estimates: Memory=27.00MB |
| |
| PLAN-ROOT SINK |
| | |
| 05:MERGING-EXCHANGE [UNPARTITIONED] |
| | order by: ds ASC |
| | |
| 02:SORT |
| | order by: ds ASC |
| | |
| 04:AGGREGATE [FINALIZE] |
| | output: count:merge(*) |
| | group by: ds |
| | |
| 03:EXCHANGE [HASH(ds)] |
| | |
| 01:AGGREGATE [STREAMING] |
| | output: sum_init_zero(default.t_ed_xxxx_newuser_read_feature_n.parquet-stats: num_rows) |
| | group by: ds |
| | |
| 00:SCAN HDFS [default.t_ed_xxxx_newuser_read_feature_n] |
| partitions=372/372 files=2562 size=15.15GB |
+----------------------------------------------------------------------------------------------+
自底向上读取EXPLAIN的输出:
- 00阶段: 显示了底层的详细信息,如:扫描的表,表的分区数,文件数以及文件大小等信息,根据这些信息,你可以估算大概的耗时
- 01阶段: 聚合操作SUM并行地在不同的节点上执行
- 03阶段: 将01阶段的结果进行传输
- 04阶段: 将SUM结果进行合并
- 02阶段: 排序操作并行地在不同的节点中进行
- 05阶段: 排序结果合并,并且输出
EXPLAIN
也会在PROFILE
结果的头部输出。
使用SUMMARY 报告进行性能调优
SUMMARY
命令可以输出每一阶段的耗时,可以快速地了解查询的性能瓶颈,与PROFILE
输出一样,它只能在查询之后才可用,并且显示实际的时间消耗。SUMMARY
输出也会在PROFILE的头部输出的显示。
[9-24-143-25:21000] > select ds,count(*) from t_ed_xxxx_newuser_read_feature_n group by ds order by ds;
[9-24-143-25:21000] > summary;
+---------------------+--------+----------+----------+-------+------------+----------+---------------+--------------------------------------------+
| Operator | #Hosts | Avg Time | Max Time | #Rows | Est. #Rows | Peak Mem | Est. Peak Mem | Detail |
+---------------------+--------+----------+----------+-------+------------+----------+---------------+--------------------------------------------+
| 05:MERGING-EXCHANGE | 1 | 3.20s | 3.20s | 372 | 372 | 0 B | 0 B | UNPARTITIONED |
| 02:SORT | 51 | 517.22us | 2.54ms | 372 | 372 | 6.02 MB | 6.00 MB | |
| 04:AGGREGATE | 51 | 1.75ms | 7.85ms | 372 | 372 | 2.12 MB | 10.00 MB | FINALIZE |
| 03:EXCHANGE | 51 | 2.91s | 3.10s | 2.44K | 372 | 0 B | 0 B | HASH(ds) |
| 01:AGGREGATE | 51 | 135.29ms | 474.62ms | 2.44K | 372 | 2.03 MB | 10.00 MB | STREAMING |
| 00:SCAN HDFS | 51 | 1.08s | 2.58s | 2.56K | 96.53M | 1.05 MB | 1.00 MB | default.t_ed_xxxx_newuser_read_feature_n |
+---------------------+--------+----------+----------+-------+------------+----------+---------------+--------------------------------------------+
使用PROFILE进行性能分析
PROFILE
语句将产生一个关于最近一次查询的底层报告的详细信息展示。与EXPLAIN
不同,这些信息只在查询完成之后才会生成,它显示了每个节点上的物理详细信息如:读取的字节数,最大内存消耗等。
你可以根据这些信息来确定查询时I/O密集型,还是CPU密集型,网络是否导致瓶颈,是否某些节点性能差但是其它节点性能好等信息。