Impala基础知识

概述

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

Impala基础知识_第1张图片
不同点:

  1. Hive适合于长时间的批处理查询分析,而Impala适合于实时交互式SQL查询
  2. Hive依赖于MapReduce计算框架,Impala把执行计划表现为一棵完整的执行计划树,直接分发执行计划到各个Impalad执行查询
  3. Hive在执行过程中,如果内存放不下所有数据,则会使用外存,以保证查询能顺序执行完成,Impala在遇到内存放不下数据时,不会利用外存,所以Impala目前处理查询时会受到一定的限制

相同点:

  1. Hive与Impala使用相同的存储数据池,都支持把数据存储于HDFS和HBase中
  2. Hive与Impala使用相同的元数据
  3. Hive与Impala中对SQL的解释处理比较相似,都是通过词法分析生成执行计划

总结:

  1. Impala的目的不在于替换现有的MapReduce工具
  2. 把Hive与Impala配合使用效果最佳
  3. 可以先使用Hive进行数据转换处理,再使用Impala在Hive处理后的结果数据集上进行快速的数据分析

架构

Impala基础知识_第2张图片
Impala和Hive、HDFS、HBase等工具是统一部署在一个Hadoop平台上的,主要由Impalad,State Store和CLI三部分组成:

  1. Impalad
    • 负责协调客户端提交的查询的执行
    • 包含Query Planner、Query Coordinator和Query Exec Engine三个模块
    • 与HDFS的数据节点(HDFS DN)运行在同一节点上
    • 给其他Impalad分配任务以及收集其他Impalad的执行结果进行汇总
    • Impalad也会执行其他Impalad给其分配的任务,主要就是对本地HDFS和HBase里的部分数据进行操作
  2. State Store
    创建一个statestored进程,负责收集分布在集群中各个Impalad进程的资源信息,用于查询调度。
  3. CLI
    给用户提供查询使用的命令行工具,还提供Hue、JDBC及ODBC的使用接口。

说明:Impala中的元数据直接存储在Hive中。Impala采用与Hive相同的元数据、SQL语法、ODBC驱动程序和用户接口,从而使得在一个Hadoop平台上,可以统一部署Hive和Impala等分析工具,同时支持批处理和实时查询。

查询执行过程

Impala基础知识_第3张图片
Impala执行查询的具体过程:

  1. 当用户提交查询前,Impala先创建一个负责协调客户端提交的查询的Impalad进程,该进程会向Impala State Store提交注册订阅信息,State Store会创建一个statestored进程,statestored进程通过创建多个线程来处理Impalad的注册订阅信息。
  2. 用户通过CLI客户端提交一个查询到impalad进程,Impalad的Query Planner对SQL语句进行解析,生成解析树;然后Planner把这个查询的解析树变成若干PlanFragment,发送到Query Coordinator。
  3. Coordinator通过从MySQL元数据库中获取元数据,从HDFS的名称节点中获取数据地址,以得到存储这个查询相关数据的所有数据节点。
  4. Coordinator初始化相应impalad上的任务执行,即把查询任务分配给所有存储这个查询相关数据的数据节点。
  5. Query Executor通过流式交换中间输出,并由Query Coordinator汇聚来自各个impalad的结果。
  6. 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)

区别:

  1. 内部表的数据是由Hive自身管理的,外部表的数据是由HDFS管理的;
  2. 删除内部表会删除存储在hive元数据库的元数据和存储在HDFS的文件数据;删除外部表只删除元数据不删除存储的数据;
  3. 两者都可以在建表的时候指定location,指定数据文件的存放位置;如果不指定的话,默认都是在/user/hive/warehouse/目录下(这个目录是可以在配置文件中修改的)。
  4. 两者的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算法有两类:

  1. hash join:对于等值join,Impala将采用hash的方式处理,具体又分两种策略:broadcast 和 Shuffle。
    1. broadcast join 非常适合右表是小表的情形,Impala先将右表复制到各个节点,再和左表做join
    2. shuffle join:亦partitioned join,适合大表和大表关联。partitioned join 和右表的 partition 没有直接关系,Impala会将右表打散成N份,发送到左表所在的节点,然后join;有点类似于mapreduce中的shuffle
  2. 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中每个阶段的结果集大小,而对于手动指定顺序来说,可以根据如下方式开始,然后再手动调节来达到最优:

  1. 首先指定最大的表,此表一般保存于磁盘中
  2. 指定最小的表,第二张表、第三张表等等之后的表都是通过网络传输的,你需要对这些结果集进行裁剪处理以降低传输数据量
  3. 指定次小表,再到次次小表等

如表的大小如下: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中使用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

异常

  1. set mem_limit=-1;
    取消内存限制;
  2. set DISABLE_UNSAFE_SPILLS=0/FALSE;
    设置为0/FALSE时,内存运算濒临溢出时转为磁盘运算;
    设置为1/TRUE时,当内存溢出时直接报内存溢出Memory limit exceeded错误
  3. java.sql.SQLException:memory limit exceeded常见原因:

优化技巧

在优化之前,可先拿到查询计划,类似mysql explain查询计划。在执行后也可以查看详细的执行信息。

查询计划

Impala提供三种方式得知查询计划

  1. EXPLAIN:获取执行计划,而无须真正的执行query
  2. PROFILE:产生一个关于最近一次查询的底层报告的详细信息展示。与EXPLAIN不同,这些信息只在查询完成之后才会生成,它显示每个节点上的物理详细信息如:读取的字节数,最大内存消耗等。
    想要查看一个查询的物理性能特性的概览,可以在执行查询之后立马在impala-shell中执行PROFILE命令,输出的信息中将展示哪个阶段耗时最多,以及每一阶段估算的内存消耗、行数与实际的差异。进行性能分析,可根据这些信息来确定查询时I/O密集型,还是CPU密集型,网络是否导致瓶颈,是否某些节点性能差但是其它节点性能好等信息。
  3. 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:查看查询时间及占用内存
区别不重要,都可用。

除了查询计划,最佳实践:

  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. 选择合适的join算法

具体地:

  1. 最大的表应该放在表清单的最左边
  2. 多个join的查询语句,应该将选择性最强的join放在最前面
  3. 定期对表收集统计信息或在大量DML操作后主动收集统计信息
  4. 在单一join查询中,涉及到的数据表个数尽量不要超过4个,不然效率比较低下

奇技淫巧

Excel连接Impala

参考Excel连接Impala

进阶

RuntimeFilter

RuntimeFilter 是Impala 2.5及更高版本中可用的优化特性 。当针对分区表进行查询,或join条件仅需要表中的一小部分数据时,Impala会在查询运行时确定适当的条件,并将该信息广播到所有正在读取数据的impalad节点,以便它们可以避免不必要的IO,并仅输出与之匹配的数据子集来避免不必要的网络传输。

Impala高性能探秘之Runtime Filter
kudu pk parquet—runtime filter实践
Impala - Runtime Filter的原理及实现

参考

  • Impala技术原理
  • Impala的命令COMPUTE STATS
  • Impala性能优化总结
  • Apache Impala 性能优化
  • impala_describe

你可能感兴趣的:(大数据,impala)