概述
Impala是由Cloudera公司开发的新型查询系统,参照Dremel系统进行设计的。提供SQL语义,能查询存储在Hadoop的HDFS和HBase上的PB级大数据,在性能上比Hive高出3~30倍。
基于Hive的大数据实时分析查询引擎,其运行需要依赖于Hive元数据。Impala采用与商用并行关系数据库类似的分布式查询引擎,可以直接与HDFS和HBase进行交互查询。Impala和Hive采用相同的SQL语法、ODBC驱动程序和用户接口。Impalad核心进程,负责接收查询请求并向多个数据节点分发任务。statestored进程负责监控所有Impalad进程,并向集群中的节点报告各个Impalad进程的状态。catalogd进程负责广播通知元数据的最新信息
特性:
- 支持Parquet、Avro、Text、RCFile、SequenceFile等多种文件格式;
- 支持存储在HDFS、HBase、Amazon S3上的数据操作;
- 支持多种压缩编码方式:Snappy、Gzip、Deflate、Bzip2、LZO;
- 支持UDF和UDAF;
- 自动以最有效的顺序进行表连接;
- 允许定义查询的优先级排队策略;
- 支持多用户并发查询;
- 支持数据缓存;
- 提供计算统计信息(COMPUTE STATS);
- 提供窗口函数(聚合 OVER PARTITION, RANK, LEAD, LAG, NTILE等等)以支持高级分析功能;
- 支持使用磁盘进行连接和聚合,当操作使用的内存溢出时转为磁盘操作;
- 允许在where子句中使用子查询;
- 允许增量统计——只在新数据或改变的数据上执行统计计算;
- 支持maps、structs、arrays上的复杂嵌套查询;
- 可以使用impala插入或更新HBase。
劣势:
- Impala不提供任何对序列化和反序列化的支持;
- Impala只能读取文本文件,而不能读取自定义二进制文件;
- 每当新的记录/文件被添加到HDFS中的数据目录时,该表需要被刷新。会导致正在执行的查询sql遇到刷新会挂起,查询不动。
对比Hive
不同点:
- Hive适合于长时间的批处理查询分析,而Impala适合于实时交互式SQL查询
- Hive依赖于MapReduce计算框架,Impala把执行计划表现为一棵完整的执行计划树,直接分发执行计划到各个Impalad执行查询
- Hive在执行过程中,如果内存放不下所有数据,则会使用外存,以保证查询能顺序执行完成,Impala在遇到内存放不下数据时,不会利用外存,所以Impala目前处理查询时会受到一定的限制
相同点:
- Hive与Impala使用相同的存储数据池,都支持把数据存储于HDFS和HBase中
- Hive与Impala使用相同的元数据
- Hive与Impala中对SQL的解释处理比较相似,都是通过词法分析生成执行计划
总结:
- Impala的目的不在于替换现有的MapReduce工具
- 把Hive与Impala配合使用效果最佳
- 可以先使用Hive进行数据转换处理,再使用Impala在Hive处理后的结果数据集上进行快速的数据分析
架构
Impala和Hive、HDFS、HBase等工具是统一部署在一个Hadoop平台上的,主要由Impalad,State Store和CLI三部分组成:
- Impalad
- 负责协调客户端提交的查询的执行
- 包含Query Planner、Query Coordinator和Query Exec Engine三个模块
- 与HDFS的数据节点(HDFS DN)运行在同一节点上
- 给其他Impalad分配任务以及收集其他Impalad的执行结果进行汇总
- Impalad也会执行其他Impalad给其分配的任务,主要就是对本地HDFS和HBase里的部分数据进行操作
- State Store
创建一个statestored进程,负责收集分布在集群中各个Impalad进程的资源信息,用于查询调度。 - CLI
给用户提供查询使用的命令行工具,还提供Hue、JDBC及ODBC的使用接口。
说明:Impala中的元数据直接存储在Hive中。Impala采用与Hive相同的元数据、SQL语法、ODBC驱动程序和用户接口,从而使得在一个Hadoop平台上,可以统一部署Hive和Impala等分析工具,同时支持批处理和实时查询。
查询执行过程
Impala执行查询的具体过程:
- 当用户提交查询前,Impala先创建一个负责协调客户端提交的查询的Impalad进程,该进程会向Impala State Store提交注册订阅信息,State Store会创建一个statestored进程,statestored进程通过创建多个线程来处理Impalad的注册订阅信息。
- 用户通过CLI客户端提交一个查询到impalad进程,Impalad的Query Planner对SQL语句进行解析,生成解析树;然后Planner把这个查询的解析树变成若干PlanFragment,发送到Query Coordinator。
- Coordinator通过从MySQL元数据库中获取元数据,从HDFS的名称节点中获取数据地址,以得到存储这个查询相关数据的所有数据节点。
- Coordinator初始化相应impalad上的任务执行,即把查询任务分配给所有存储这个查询相关数据的数据节点。
- Query Executor通过流式交换中间输出,并由Query Coordinator汇聚来自各个impalad的结果。
- Coordinator把汇总后的结果返回给CLI客户端。
基本概念
函数
Hive查看所有内置函数:
show functions;-- hive仅显示函数的名称, 没有参数和返回值信息
desc function <function_name>;-- 该命令能显示函数的具体用途
Impala支持java/c++编写UDF,impala内置很多UDF,查看内置UDF方法:
use _impala_builtins;-- 自带数据库
show functions;-- 查看内置UDF,会显示完整的函数签名信息
功能函数
cast(expr AS type)
:类型转换函数,如将number转成string或相反
typeof()
:该函数可以用来检查其他函数返回值类型,hive 没有对应的函数
version()
:返回 impalad 版本
pid()
:client连接到impalad 的 pid
user()
:返回连接连接 impalad 的 linux username
effective_user()
:一般情况下和和 user() 结果相同, 如果启用 delegation, effective_user() 返回代理 user id
uuid()
:返回一个唯一的 guid 值
impala 没有 sequence概念,但可使用concat(cast(unix_timestamp() as string), uuid())
, 前面加上时间戳, 可以保证大概的顺序.
条件函数
coalesce(type v1, type v2, ...); -- 返回第一个非null的v,全部为null则返回null
decode(); -- 同Oracle的decode()函数
if(boolean condition, type ifTrue, type ifFalseOrNull); --如果第一参数为true,结果为第2个参数, 否则为第三个参数
ifnull(type a, type ifNull); -- 同nvl()
isnull(type a, type ifNull); --同nvl()
nullif(expr1,expr2); -- 两参数相等,返回null
nullifzero(numeric_expr); --参数为0,返回null
nullvalue(expression); --如果为null,返回true
nvl(type a, type ifNull); --如果第一个参数为null,结果为第二个参数,否则为第一个参数
nvl2(type a, type ifNull, type ifNotNull); --如果第一个参数为null,结果为第2个参数,否则为第3个参数
zeroifnull(numeric_expr);
nonnullvalue(expression);
isfalse(boolean);
isnotfalse(boolean);
isnottrue(boolean);
istrue(boolean);
其他
describe formatted table
:可查询impala某个表的信息,比如 CreateTime 表示表的生成时间;
元数据与数据
元数据是记录数据的数据。Impala的数据就是文件,而元数据是记录文件存在什么位置,多少个,大小,时间等。
刷新
invalidate metadata和refresh
refresh轻量级,适用于数据更新(不是Impala途径增加或者删除数据)的场景;
invalidate metadata,适用于表结构发生改变(非Impala途径创建或者修改表结构);
统计信息
收集统计信息:compute stats db.table
查看表统计信息:show table stats db.table
查看字段统计信息:show column stats db.table
用途:
join query缺少统计信息时,可能会生成错误的执行计划,查询缓慢;
建表语句
建表语句中的location指向实际数据的路径;
了解一个表的基本类别可以通过show create table
命令;
删除impala的一行数据:不是delete
外部表
创建表时可通过指定location来指定表文件的存放路径,如果不指定的话,默认是将数据存放在/user/hive/warehouse/库名
下。未被external修饰的表是内部表(managed table),被external修饰的是外部表(external table)
区别:
- 内部表的数据是由Hive自身管理的,外部表的数据是由HDFS管理的;
- 删除内部表会删除存储在hive元数据库的元数据和存储在HDFS的文件数据;删除外部表只删除元数据不删除存储的数据;
- 两者都可以在建表的时候指定location,指定数据文件的存放位置;如果不指定的话,默认都是在
/user/hive/warehouse/目录
下(这个目录是可以在配置文件中修改的)。 - 两者的load操作都会移动数据
统计信息
收集信息使用COMPUTE STATS
命令,查看信息使用show table/column stats
。
COMPUTE STATS
和HIVE的ANALYZE TABLE类似,这个命令主要也是为了优化查询。本来IMPALA是依靠HIVE的ANALYZE TABLE的,但是这个命令不是很好用同时不稳定,所以IMPALA自己实现个命令完成相同功能。
有两类,语法:
# 全量
COMPUTE STATS [db_name.]table_name
# 增量
COMPUTE INCREMENTAL STATS [db_name.]table_name [PARTITION (partition_spec)]
作用:
收集有关表中数据的容量和分布以及所有相关列和分区的信息。这些信息存储在metastore数据库中,Impala使用这些信息来帮助优化查询。
区别:
COMPUTE STATS适用于全量,操作更重量级,适合非分区表;
COMPUTE INCREMENTAL STATS适用于增量,操作轻量级,适合分区表;
show table/column stats
> show table stats t2;
| #Rows | #Files | Size | Format |
±------±-------±---------±-------+
| -1 | 28 | 960.00KB | TEXT |
> show column stats t1;
| Column | Type | #Distinct Values | #Nulls | Max Size | Avg Size |
±-------±-------±-----------------±-------±---------±---------+
| id | INT | -1 | -1 | 4 | 4 |
| s | STRING | -1 | -1 | -1 | -1 |
join
join算法有两类:
- hash join:对于等值join,Impala将采用hash的方式处理,具体又分两种策略:broadcast 和 Shuffle。
- broadcast join 非常适合右表是小表的情形,Impala先将右表复制到各个节点,再和左表做join
- shuffle join:亦partitioned join,适合大表和大表关联。partitioned join 和右表的 partition 没有直接关系,Impala会将右表打散成N份,发送到左表所在的节点,然后join;有点类似于mapreduce中的shuffle
- nested loop join:针对非等值join,Impala将使用 nested loop join,这时不能设置 SHUFFLE/BROADCAST hint,也不能使用 spill disk 功能。Impala的非等值join的效率较低,Vertica的效率非常高,Hive直接不支持
broadcast vs shuffle
broadcast,广播连接,Impala默认方式,大表一定要放在左边,因为impala在广播右侧表,所有右侧表会复制到需要右侧表进行联接的所有节点。右侧的表被认为比左侧的表小,并且它的内容被发送到查询涉及到的其他节点上。
在join后面加[shuffle],将broadcast join转换为shuffle join,
替代的技术称作分割连接(partitioned join,与分区表无关),更适用于近乎相同大小的大型表的连接,每一个表的部分内容被发送到对应的其他节点,然后这些行的子集可以并行处理。广播和分区连接的选择仍然依赖于连接中所有表的可用的、使用 COMPUTE STATS 语句的统计信息。
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_JOI技术时,须手动指定join查询中表的顺序而不是依赖于Impala优化器。Impala优化器使用特殊的手段来估算join中每个阶段的结果集大小,而对于手动指定顺序来说,可以根据如下方式开始,然后再手动调节来达到最优:
- 首先指定最大的表,此表一般保存于磁盘中
- 指定最小的表,第二张表、第三张表等等之后的表都是通过网络传输的,你需要对这些结果集进行裁剪处理以降低传输数据量
- 指定次小表,再到次次小表等
如表的大小如下:BIG、MEDIUM、SMALL和TINY,那顺序应该如此:BIG join TINY join SMALL join MEDIUM。
Impala查询优化器根据表的绝对或者相对大小来选择不同技术来执行join查询。
broadcast或者partition join的选择是根据compute stats采集到的可用统计指标来衡量的。对于指定查询语句,可以通过执行EXPLAIN就可以查看选用的是哪个join策略。
当join中表或者列的统计指标不可用时,Impala将无统计指标的表认为统计指标都为0,这些表都将作为右表处理。
分区表
存储格式
通常对于大数据量来说,Parquet文件格式是最佳的
操作符
Impala特有操作符
ILIKE:忽略大小写的 like 操作符
REGEXP:正则匹配操作符
RLIKE:同 REGEXP 操作符
IREGEXP:忽略大小写的正则匹配符
IS DISTINCT FROM:判断前后两个表达式是否不相等,和<>
操作符类似,但 null IS DISTINCT FROM null 返回 false
IS not DISTINCT FROM:判断前后两个表达式是否相等,和=操作符类似,唯一不同的是,处理 null 时候,null IS not DISTINCT FROM null 结果为 ture
异常
set mem_limit=-1;
取消内存限制;set DISABLE_UNSAFE_SPILLS=0/FALSE;
设置为0/FALSE
时,内存运算濒临溢出时转为磁盘运算;
设置为1/TRUE
时,当内存溢出时直接报内存溢出Memory limit exceeded
错误java.sql.SQLException:memory limit exceeded
常见原因:
优化技巧
在优化之前,可先拿到查询计划,类似mysql explain
查询计划。在执行后也可以查看详细的执行信息。
查询计划
Impala提供三种方式得知查询计划
- EXPLAIN:获取执行计划,而无须真正的执行query
- PROFILE:产生一个关于最近一次查询的底层报告的详细信息展示。与EXPLAIN不同,这些信息只在查询完成之后才会生成,它显示每个节点上的物理详细信息如:读取的字节数,最大内存消耗等。
想要查看一个查询的物理性能特性的概览,可以在执行查询之后立马在impala-shell中执行PROFILE命令,输出的信息中将展示哪个阶段耗时最多,以及每一阶段估算的内存消耗、行数与实际的差异。进行性能分析,可根据这些信息来确定查询时I/O密集型,还是CPU密集型,网络是否导致瓶颈,是否某些节点性能差但是其它节点性能好等信息。 - SUMMAY:输出每一阶段的耗时,可以快速地了解查询的性能瓶颈,SUMMARY输出也会在PROFILE的头部输出的显示。
想要了解查询的详细性能特征,可以在执行查询之后立马在impala-shell中执行PROFILE命令,这些底层的信息包括内存、CPU、I/O以及网络消耗的详细信息,只能在一个真实的查询之后才可用。
EXPLAIN语句概述了查询将执行的逻辑步骤,例如如何在节点间分配工作以及中间结果如何合并为最终结果, 这些你都可以在查询真正执行之前获得,你可以使用这些信息来检查查询是否会以某种非高效的方式执行。
explain select ds,count(*) from t_ed_xxxx_newuser_read_feature_n group by ds order by ds;
| 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命令可以输出每一阶段的耗时,可以快速地了解查询的性能瓶颈,与PROFILE输出一样,它只能在查询之后才可用,并且显示实际的时间消耗。SUMMARY输出也会在PROFILE的头部输出的显示。
select ds,count(*) from t_ed_xxxx_newuser_read_feature_n group by ds order by ds;
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和SUMMAY区别
profile:输出底层信息计划
summary:查看查询时间及占用内存
区别不重要,都可用。
除了查询计划,最佳实践:
- 为数据存储选择合适的文件格式(如Parquet),通常对于大数据量来说,Parquet文件格式是最佳
- 防止入库时产生大量的小文件(
insert ... values
会产生大量小文件,应该避免使用)
在impala外生成数据时,最好是text格式或Avro,可逐行的构建文件,到impala后再通过简单的insert ... select
语句将其转换为Parquet格式. - 根据实际的数据量大小选择合适的分区粒度
合适的分区策略可以对数据进行物理拆分,查询时可以忽略掉无用数据,提高查询效率,通常建议分区数量在3万以下(太多的分区也会造成元数据管理的性能下降) - 为分区key选择最小的整数类型
虽然使用string类型也可以作为分区key,因为分区key最后都是作为HDFS目录使用,但是使用最小的整数类型作为分区key可以降低内存消耗 - 选择合适的Parquet块大小
默认情况下,Impala的insert ... select
语句创建的Parquet文件都是每个分区256M(在2.0之后改为1G),通过Impala写入的Parquet文件只有一个块,因而只能被一个机器当作一个单元进行处理。如果在你的Parquet表中只有一个或者几个分区,或者一个查询只能访问一个分区,那么你的性能会非常慢,因为没有足够的数据来利用Impala并发分布式查询的优势。 - 在追求性能或者大数据量查询时,要先获取所需要的表的统计指标(如执行
compute stats
) - 减少传输到client端的数据量,如:使用聚合(如 count、sum、max等)、过滤(如WHERE)、LIMIT
结果集禁止使用美化格式进行展示(在通过impala-shell展示结果时,添加这些可选参数:-B, --output_delimiter
) - 选择合适的join算法
具体地:
- 最大的表应该放在表清单的最左边
- 多个join的查询语句,应该将选择性最强的join放在最前面
- 定期对表收集统计信息或在大量DML操作后主动收集统计信息
- 在单一join查询中,涉及到的数据表个数尽量不要超过4个,不然效率比较低下
奇技淫巧
Excel连接Impala
进阶
RuntimeFilter
RuntimeFilter 是Impala 2.5及更高版本中可用的优化特性 。当针对分区表进行查询,或join条件仅需要表中的一小部分数据时,Impala会在查询运行时确定适当的条件,并将该信息广播到所有正在读取数据的impalad节点,以便它们可以避免不必要的IO,并仅输出与之匹配的数据子集来避免不必要的网络传输。
Impala高性能探秘之Runtime Filter
kudu pk parquet—runtime filter实践
Impala - Runtime Filter的原理及实现