Impala 性能调整
下面的章节介绍影响 Impala 功能性能的各种因素,并对 Impala 查询和其他 SQL 操作进行性能调整、监控和基准测试。
这一章节同样描述了最大化 Impala 可扩展性的技术。可扩展性与性能相关:它意味着当系统负载增加时仍保持高性能(Scalability is tied to performance: it means that performance remains high as the system workload increases)。例如,减少查询的硬盘 I/O 可以加快单个的查询,与此同时,导致可以同时运行更多查询,从而提升了可扩展性。有时候,一种优化技术提升了性能的同时更增加了可扩展性。例如,减少查询的内存使用可能不会很大的提高查询性能,但是通过允许同时运行更多的 Impala 查询或其他类型的作业而不会耗尽内存,从而提升了可扩展性。
![13155539_J8dz.jpg](https://i-blog.csdnimg.cn/blog_migrate/dff65c7b8348f55de11c7808ebdeddb1.jpeg)
在开始任何性能调整和基准测试之前,请确保你的系统已经按照 Post-Installation Configuration for Impala 中的设置进行配置。
- Partitioning. 这一技术基于频繁查询的列上的不同的值,把数据物理拆分开来,允许查询跳过读取表中很大部分的数据
- Performance Considerations for Join Queries. 相对于修改物理因素,如文件格式或硬件配置,连接是你可以在 SQL 层级进行调整的主要方面(Joins are the main class of queries that you can tune at the SQL level, as opposed to changing physical factors such as the file format or the hardware configuration)。对于连接的性能,相关的主题 Column Statistics 和 Table Statistics 同样重要
- Table Statistics and Column Statistics. 使用 COMPUTE STATS 语句,采集表和列的统计信息,帮助 Impala 自动优化连接查询的性能,而不需要修改 SQL 查询语句(在 Impala 1.2.2 及以上版本,这一过程特别简单,因为 COMPUTE STATS 语句在同一个语句中同时采集两种信息,而且不需要像之前在 Hive 中运行 ANALYZE TABLE 语句那样,不再需要执行任何设置和配置)
- Testing Impala Performance. 在进行任何基准测试之前,执行一些安装后测试(post-setup testing),以确保 Impala 使用了性能最优的设置
- Benchmarking Impala Queries. 用于 Impala 初始实验的配置和样本数据通常不适合进行性能测试(The configuration and sample data that you use for initial experiments with Impala is often not appropriate for doing performance tests)
- Controlling Resource Usage. 更多内存,更加性能(The more memory Impala can utilize, the better query performance you can expect)。在一个同样运行其他负载的集群中,你必须权衡考虑,保证所有 Hadoop 组件具有能良好运行的足够内存,因此你可能限制 Impala 可使用的内存
分区
表的所有数据文件默认放在一个目录下。分区是一项基于一个或多个上的值,在载入时物理拆分数据的技术。例如,对于根据 year 列分区的 school_records 表来说,对于每一个不同的年份都有一个单独的数据目录,并且这一年的所有数据都存放在这个目录下的数据文件中。一个包含 WHERE 条件如 YEAR=1966, YEAR IN (1989,1999), YEAR BETWEEN 1984 AND 1989 的查询,可以只从对应的一个或多个目录下检索数据文件,极大的减少了读取和测试的数据的数量。
分区通常对应:
- 非常大的表,完整读取整个数据集花费的时间不可想象(where reading the entire data set takes an impractical amount of time)
- 全部或几乎所有的查询都包含分区列查询条件的表。我们上面的例子中那个根据 year 分区的表, SELECT COUNT(*) FROM school_records WHERE year = 1985 是高效的,只检索数据的一小部分;但是 SELECT COUNT(*) FROM school_records 则必须处理每一年的单独的数据文件,导致必未分区表更多的工作。假如你频繁基于 last name, student ID, 等等不检测年份的对表进行查询,考虑不分区表
- 列包含合理的基数(cardinality--不同值的个数)。假如列只包括少量的值,如 Male 或 Female,你无法通过对每一个查询消除大约 50% 数据的读取来获得更高的效率。假如列的每一个值只对应很少的行,要处理的目录的数量会变成一个限制因素,并且每一个目录中的数据文件可能太小了,无法从 Hadoop 以 multi-megabyte 块传输数据的机制受益。例如,你可能用年来分区人口数据,用年和月来存放销售数据,用年月日来分区网络流量数据(一些更高流量的用户甚至用小时和分钟来分区数据)
- 总是使用抽取、转换、加载(ETL)管道加载的数据。分区列的值从原始的数据文件剥离,并对应到目录名中,因此加载数据到分区表涉及了某种转换或预处理(The values of the partitioning columns are stripped from the original data files and represented by directory names, so loading data into a partitioned table involves some sort of transformation or preprocessing)
在 Impala SQL 语法中,分区会影响到这些语句:
- CREATE TABLE: 在创建表时,使用 PARTITIONED BY 子句来标识分区列的名称和数据类型。表中的列不包括这些分区列
- ALTER TABLE: 可以添加或删除分区,用于处理海量数据集的不同部分。对于根据日期值分区的数据,你可以不再保留"过期(age out)"的数据
- INSERT: 当向分区表插入数据时,需要标识分区列。对于插入的每一行,分区列的值没有保存在数据文件里,而是根据行存储的目录名称确定。也可以使用 INSERT OVERWRITE 语句来加载一组数据到指定的分区;你可以替换指定分区的内容但是不能向指定分区追加数据
- 尽管表分区与否的 SELECT 语句的语法相同,对分区表的查询方式可能对性能和可扩展性产生戏剧性的影响。在查询过程中,让查询跳过某些分区的机制称作分区修剪(partition pruning);参见 Partition Pruning for Queries 了解详细信息
参见 Attaching an External Partitioned Table to an HDFS Directory Structure 中的例子,演示了创建分区表的语法,HDFS 中底层的目录结构,以及如何连接到 Impala 外部分区表中存出来 HDFS 其他位置的数据文件(how to attach a partitioned Impala external table to data files stored elsewhere in HDFS)
参见 Partitioning for Parquet Tables 了解 Parquet 分区表的性能注意事项。
参见 NULL 了解分区表中 NULL 值如何对应。
针对查询进行分区修剪(Partition Pruning)
分区修剪(Partition pruning)指的是一种查询可以跳过一个或多个分区对应的数据文件不进行读取的技术。假如你能安排你的查询从查询计划中剪除大量的不必要的分区,查询使用更少的资源,因此与剪除的不必要的分区成比例的变快,并且更可扩展(If you can arrange for queries to prune large numbers of unnecessary partitions from the query execution plan, the queries use fewer resources and are thus proportionally faster and more scalable)。
例如,如果一个表使用 YEAR, MONTH, DAY 分区,这样如 WHERE year = 2013, WHERE year < 2010, WHERE year BETWEEN 1995 AND 1998 等 WHERE 子句允许 Impala 除了指定范围的分区外,跳过所有其他分区的数据文件。同样的,WHERE year = 2013 AND month BETWEEN 1 AND 3 甚至可以剪除更多的分区,只读取一年中的一部分数据文件。
在执行查询之前,通过检查 EXPLAIN 查询的输出来检查查询分区修剪的效果。例如,下面例子中的表有 3 个分区,而查询只读取其中 1 个。EXOLAIN 计划中的标识符 #partitions=1/3 证明 Impala 可以进行对应的分区修剪。
[localhost:21000] > insert into census partition (year=2010) values ('Smith'),('Jones');
[localhost:21000] > insert into census partition (year=2011) values ('Smith'),('Jones'),('Doe');
[localhost:21000] > insert into census partition (year=2012) values ('Smith'),('Doe');
[localhost:21000] > select name from census where year=2010;
+-------+
| name |
+-------+
| Smith |
| Jones |
+-------+
[localhost:21000] > explain select name from census where year=2010;
+------------------------------------------------------------------+
| Explain String |
+------------------------------------------------------------------+
| PLAN FRAGMENT 0 |
| PARTITION: UNPARTITIONED |
| |
| 1:EXCHANGE |
| |
| PLAN FRAGMENT 1 |
| PARTITION: RANDOM |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 1 |
| UNPARTITIONED |
| |
| 0:SCAN HDFS |
| table=predicate_propagation.census #partitions=1/3 size=12B |
+------------------------------------------------------------------+
通过WHERE 子句中其他部分的中间属性,甚至在分区键列没有明确指定常量值的时候,Impala 都可以进行分区修剪(Impala can even do partition pruning in cases where the partition key column is not directly compared to a constant, by applying the transitive property to other parts of the WHERE clause)。这一技术称为谓词传播(predicate propagation),自 Impala 1.2.2 开始可用。在下面例子里,表 census 中包含另一个列存放数据收集的时间(是 10 年采集的)。即使分区键列 (YEAR) 没有对应一个常量, Impala 也可以推断只有 YEAR=2010 分区是必需的,并再次只读取了总分区中 1/3 个分区。
[localhost:21000] > drop table census;
[localhost:21000] > create table census (name string, census_year int) partitioned by (year int);
[localhost:21000] > insert into census partition (year=2010) values ('Smith',2010),('Jones',2010);
[localhost:21000] > insert into census partition (year=2011) values ('Smith',2020),('Jones',2020),('Doe',2020);
[localhost:21000] > insert into census partition (year=2012) values ('Smith',2020),('Doe',2020);
[localhost:21000] > select name from census where year = census_year and census_year=2010;
+-------+
| name |
+-------+
| Smith |
| Jones |
+-------+
[localhost:21000] > explain select name from census where year = census_year and census_year=2010;
+------------------------------------------------------------------+
| Explain String |
+------------------------------------------------------------------+
| PLAN FRAGMENT 0 |
| PARTITION: UNPARTITIONED |
| |
| 1:EXCHANGE |
| |
| PLAN FRAGMENT 1 |
| PARTITION: RANDOM |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 1 |
| UNPARTITIONED |
| |
| 0:SCAN HDFS |
| table=predicate_propagation.census #partitions=1/3 size=22B |
| predicates: census_year = 2010, year = census_year |
+------------------------------------------------------------------+
在执行查询之后,立刻检查 PROFILE 语句的输出,了解实际读取和处理的数据量更详细的分析。
假如是在分区表上建立的视图,所有的分区修剪都是由原使得查询子句确定。即使在试图上的查询包含了引用分区键列的 WHERE 子句,Impala 不会修剪添加的列(If a view applies to a partitioned table, any partition pruning is determined by the clauses in the original query. Impala does not prune additional columns if the query on the view includes extra WHEREclauses referencing the partition key columns)。
分区键列
你选择的分区列应当是那种经常在重要的、大型的查询中过滤查询结果的列。通常来说,数据与时间值有关时,使用年、月、日的组合作为分区列,数据与一些位置有关时使用地理区域作为分区列。
- 对于基于时间的数据,拆分出其中的各个部分到单独的列,因为 Impala 不能基于 TIMESTAMP 列进行分区
- 分区列的数据类型对存储需求方面没有明显的影响,因为分区列的值不是存放在数据文件里,而是在 HDFS 目录名对应的字符串里
- Remember that when Impala queries data stored in HDFS, it is most efficient to use multi-megabyte files to take advantage of the HDFS block size. 对于 Parquet 表,块大小 (数据文件理想大小) 是 1GB。因此,应避免指定太多分区键列,这样会导致个别分区只包含少量数据。例如,假如你每天获取 1GB 数据,你可能使用年、月、日进行分区;当你每分钟获取 5GB 数据时,你可能使用年、月、日、时、分来分区。假如你的数据保护地理组件,假如你每个邮编都有很多M的数据时,你可以基于邮编分区;假如不是,那么你可能需要使用更大的区域,如 city, state, 或 country. state 分区。
为分区设置不同的文件格式
分区表具有为不同的分区设置不同的文件格式的灵活性。例如,你原来是接收文本格式数据,然后是 RCFile 格式,最终会接收 Parquet 格式,所有这些数据可以存放在同一个表里进行查询。你只需要确保该表的结构是使用不同文件格式的的数据文件分别在单独的分区。
例如,下面是当你收到不同年份的数据时,你可能从文本切换到 Parquet:
[localhost:21000] > create table census (name string) partitioned by (year smallint);
[localhost:21000] > alter table census add partition (year=2012); -- Text format;
[localhost:21000] > alter table census add partition (year=2013); -- Text format switches to Parquet before data loaded;
[localhost:21000] > alter table census partition (year=2013) set fileformat parquet;
[localhost:21000] > insert into census partition (year=2012) values ('Smith'),('Jones'),('Lee'),('Singh');
[localhost:21000] > insert into census partition (year=2013) values ('Flores'),('Bogomolov'),('Cooper'),('Appiah');
如上所述,HDFS 目录 year=2012 包含文本格式数据文件,而 HDFS 目录 year=2013 包含 Parquet 数据文件。一如既往,当加载实际数据时,你应当使用 INSERT ... SELECT 或 LOAD DATA 来导入大批量的数据,而不是使用产生少量的对实际查询低效的文件的 INSERT ... VALUES 语句。
对于其他的 Impala 无法本地创建的文件类型,你可以切换到 Hive 并执行 ALTER TABLE ... SET FILEFORMAT 语句,并在这里执行 INSERT 或 LOAD DATA 语句。当切换回 Impala 后,执行 REFRESH table_name 语句以便 Impala 感知到通过 Hive 添加的任意分区或新数据。
连接查询性能注意事项
涉及连接操作的查询通常比只引用单个表的查询更需要调整。连接查询结果集的最大大小是所有连接的表中行数的乘积。当连接几个百万或十亿记录的表时,任何过滤结果集的失误,或查询中其他的低效操作,都将会导致操作无法完成不得不取消 。
调整 Impala 连接查询的最简单的技术就是在参与连接的每个表上使用 COMPUTE STATS 语句采集统计信息,然后让 Impala 基于每一个表的大小、每一个列不同值的个数、等等信息自动的优化查询。COMPUTE STATS 语句和 连接优化(join optimization)是 Impala 1.2.2 引入的新功能。为了保证每个表上统计信息的精确,请在表加载数据之后执行 COMPUTE STATS 语句,并在因 INSERT, LOAD DATA, 添加分区等操作导致数据大幅变化之后再次执行。
假如连接查询中所有表的统计信息不可用,或 Impala 选择的连接顺序不是最优,你可以通过在 SELECT 关键字之后立刻紧跟 STRAIGHT_JOIN 关键字,来覆盖自动的连接顺序优化。这时候,Impala 使用表在查询中出现的顺序来指导连接如何处理。首先是最大的表,然后是次大的,依此类推。术语"最大"和"最小"指中间结果集的大小,这些基于作为结果集一部分的每个表的行数和列数(The terms "largest" and "smallest" refers to the size of the intermediate result set based on the number of rows and columns from each table that are part of the result set)。例如,如果你连接了表 sales 和 customers,查询可能是从产生了 5000 次购买的 100 个用户中查找结果集。这时候,你应该使用 SELECT ... FROM sales JOIN customers ..., 把 customers 放在右侧,因为在这个查询上下文中它更小。
依赖于表的绝对和相对的大小,Impala 查询计划器在执行连接查询的不同技术之间进行选择。广播连接(Broadcast joins) 是默认方式,右侧的表被认为比左侧的表小,并且它的内容被发送到查询涉及到的其他节点上。替代的技术称作分割连接(partitioned join) (与分区表无关),更适用于近乎相同大小的大型表的连接。使用这一技术,每一个表的部分内容被发送到对应的其他节点,然后这些行的子集可以并行处理。广播和分区连接的选择仍然依赖于连接中所有表的可用的、使用 COMPUTE STATS 语句手机的统计信息。
对查询执行 EXPLAIN 语句,查看该查询采用了哪种连接策略。如果你发现一个查询使用了广播连接,而你通过基准测试知道分割连接更高效,或者相反情况时,在查询上添加提示指定使用的精确的连接机制。参见 Hints 了解详细信息。
统计信息不可用时连接如何处理
假如连接中的一些表的表或列统计信息不可用,Impala 仍然使用可用的那部分信息重新排列表,包含可用统计信息的表放在连接的左侧,按照整体大小和基数降序排列(Tables with statistics are placed on the left side of the join order, in descending order of cost based on overall size and cardinality)。没有统计信息的表被认为大小为 0,也就是说,它们总是放置在连接查询的右侧。
Overriding Join Reordering with STRAIGHT_JOIN
假如因为过时的统计信息或意外的数据分布, Impala 连接查询很低效,你可以通过在 SELECT 关键字之后紧跟着 STRAIGHT_JOIN 关键字来重新排序连接的表,使的 Impala 高效。STRAIGHT_JOIN 关键字关闭 Impala 内部使用的连接子句的重新排序,并根据 查询中 join 子句中列出的顺序优化(The STRAIGHT_JOIN keyword turns off the reordering of join clauses that Impala does internally, and produces a plan that relies on the join clauses being ordered optimally in the query text)。这时,重写查询以便最大的表在最左侧,跟着是次大的,依此类推直到最小的表放在最右侧。
在下面的例子里,基于 BIG 表的子查询产生一个非常小的结果集,但是这个表仍被视为好像它是最大的并放置在连接顺序的第一位。为最后的连接子句使用 STRAIGHT_JOIN 关键字,防止最终的表重新排序,保持它作为最右边表的连接顺序(Using STRAIGHT_JOIN for the last join clause prevents the final table from being reordered, keeping it as the rightmost table in the join order)。
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;
Examples of Join Order Optimization
下面的例子演示了10亿、2亿、1百万行表之间的连接(这时,表都是未分区的,使用 Parquet 格式)。最小的表是最大的表的一个子集,方便起见在唯一的 ID 列上进行连接。最小的表只包含其他表中列的一个子集。
[localhost:21000] > create table big stored as parquet as select * from raw_data;
+----------------------------+
| summary |
+----------------------------+
| Inserted 1000000000 row(s) |
+----------------------------+
Returned 1 row(s) in 671.56s
[localhost:21000] > desc big;
+-----------+---------+---------+
| name | type | comment |
+-----------+---------+---------+
| id | int | |
| val | int | |
| zfill | string | |
| name | string | |
| assertion | boolean | |
+-----------+---------+---------+
Returned 5 row(s) in 0.01s
[localhost:21000] > create table medium stored as parquet as select * from big limit 200 * floor(1e6);
+---------------------------+
| summary |
+---------------------------+
| Inserted 200000000 row(s) |
+---------------------------+
Returned 1 row(s) in 138.31s
[localhost:21000] > create table small stored as parquet as select id,val,name from big where assertion = true limit 1 * floor(1e6);
+-------------------------+
| summary |
+-------------------------+
| Inserted 1000000 row(s) |
+-------------------------+
Returned 1 row(s) in 6.32s
对于任意类型的性能测试,使用 EXPLAIN 语句查看将执行的查询是如何的昂贵(expensive)而不需要实际运行它,并且启用详细的 EXPLAIN 计划包含更详细的性能导向的信息:最有趣的计划行---展示了没有统计信息的连接的表--以黑体突出,Impala 无法正确的估算处理的每个阶段中涉及的行数,通常采用广播连接机制把其中之一的表的完整数据发送到各个节点上(Impala cannot make a good estimate of the number of rows involved at each stage of processing,and is likely to stick with the BROADCAST join mechanism that sends a complete copy of one of the tables to each node)。
[localhost:21000] > set explain_level=verbose;
EXPLAIN_LEVEL set to verbose
[localhost:21000] > explain select count(*) from big join medium where big.id = medium.id;
+----------------------------------------------------------+
| Explain String |
+----------------------------------------------------------+
| Estimated Per-Host Requirements: Memory=2.10GB VCores=2 |
| |
| PLAN FRAGMENT 0 |
| PARTITION: UNPARTITIONED |
| |
| 6:AGGREGATE (merge finalize) |
| | output: SUM(COUNT(*)) |
| | cardinality: 1 |
| | per-host memory: unavailable |
| | tuple ids: 2 |
| | |
| 5:EXCHANGE |
| cardinality: 1 |
| per-host memory: unavailable |
| tuple ids: 2 |
| |
| PLAN FRAGMENT 1 |
| PARTITION: RANDOM |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 5 |
| UNPARTITIONED |
| |
| 3:AGGREGATE |
| | output: COUNT(*) |
| | cardinality: 1 |
| | per-host memory: 10.00MB |
| | tuple ids: 2 |
| | |
| 2:HASH JOIN | | | join op: INNER JOIN (BROADCAST) | | | hash predicates: |
| | big.id = medium.id | | | cardinality: unavailable | | | per-host memory: 2.00GB |
| | tuple ids: 0 1 |
| | |
| |----4:EXCHANGE |
| | cardinality: unavailable |
| | per-host memory: 0B |
| | tuple ids: 1 |
| | |
| 0:SCAN HDFS | | table=join_order.big #partitions=1/1 size=23.12GB |
| table stats: unavailable |
| column stats: unavailable |
| cardinality: unavailable | | per-host memory: 88.00MB |
| tuple ids: 0 |
| |
| PLAN FRAGMENT 2 |
| PARTITION: RANDOM |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 4 |
| UNPARTITIONED |
| |
| 1:SCAN HDFS | | table=join_order.medium #partitions=1/1 size=4.62GB |
| table stats: unavailable |
| column stats: unavailable |
| cardinality: unavailable | | per-host memory: 88.00MB |
| tuple ids: 1 |
+----------------------------------------------------------+
Returned 64 row(s) in 0.04s
采集所有表的统计信息很简单,在每一个表上执行 COMPUTE STATS 语句:
[localhost:21000] > compute stats small;
+-----------------------------------------+
| summary |
+-----------------------------------------+
| Updated 1 partition(s) and 3 column(s). |
+-----------------------------------------+
Returned 1 row(s) in 4.26s
[localhost:21000] > compute stats medium;
+-----------------------------------------+
| summary |
+-----------------------------------------+
| Updated 1 partition(s) and 5 column(s). |
+-----------------------------------------+
Returned 1 row(s) in 42.11s
[localhost:21000] > compute stats big;
+-----------------------------------------+
| summary |
+-----------------------------------------+
| Updated 1 partition(s) and 5 column(s). |
+-----------------------------------------+
Returned 1 row(s) in 165.44s
有了统计信息,Impala 可以选择更有效的连接顺序而不是按照查询中从左到右各个表的顺序,并且可以基于表的大小和行数选择广播连接或分割连接策略:
[localhost:21000] > explain select count(*) from medium join big where big.id = medium.id;
Query: explain select count(*) from medium join big where big.id = medium.id
+-----------------------------------------------------------+
| Explain String |
+-----------------------------------------------------------+
| Estimated Per-Host Requirements: Memory=937.23MB VCores=2 |
| |
| PLAN FRAGMENT 0 |
| PARTITION: UNPARTITIONED |
| |
| 6:AGGREGATE (merge finalize) |
| | output: SUM(COUNT(*)) |
| | cardinality: 1 |
| | per-host memory: unavailable |
| | tuple ids: 2 |
| | |
| 5:EXCHANGE |
| cardinality: 1 |
| per-host memory: unavailable |
| tuple ids: 2 |
| |
| PLAN FRAGMENT 1 |
| PARTITION: RANDOM |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 5 |
| UNPARTITIONED |
| |
| 3:AGGREGATE |
| | output: COUNT(*) |
| | cardinality: 1 |
| | per-host memory: 10.00MB |
| | tuple ids: 2 |
| | |
| 2:HASH JOIN |
| | join op: INNER JOIN (BROADCAST) |
| | hash predicates: |
| | big.id = medium.id |
| | cardinality: 1443004441 |
| | per-host memory: 839.23MB |
| | tuple ids: 1 0 |
| | |
| |----4:EXCHANGE |
| | cardinality: 200000000 |
| | per-host memory: 0B |
| | tuple ids: 0 |
| | |
| 1:SCAN HDFS |
| table=join_order.big #partitions=1/1 size=23.12GB |
| table stats: 1000000000 rows total |
| column stats: all |
| cardinality: 1000000000 |
| per-host memory: 88.00MB |
| tuple ids: 1 |
| |
| PLAN FRAGMENT 2 |
| PARTITION: RANDOM |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 4 |
| UNPARTITIONED |
| |
| 0:SCAN HDFS |
| table=join_order.medium #partitions=1/1 size=4.62GB |
| table stats: 200000000 rows total |
| column stats: all |
| cardinality: 200000000 |
| per-host memory: 88.00MB |
| tuple ids: 0 |
+-----------------------------------------------------------+
Returned 64 row(s) in 0.04s
[localhost:21000] > explain select count(*) from small join big where big.id = small.id;
Query: explain select count(*) from small join big where big.id = small.id
+-----------------------------------------------------------+
| Explain String |
+-----------------------------------------------------------+
| Estimated Per-Host Requirements: Memory=101.15MB VCores=2 |
| |
| PLAN FRAGMENT 0 |
| PARTITION: UNPARTITIONED |
| |
| 6:AGGREGATE (merge finalize) |
| | output: SUM(COUNT(*)) |
| | cardinality: 1 |
| | per-host memory: unavailable |
| | tuple ids: 2 |
| | |
| 5:EXCHANGE |
| cardinality: 1 |
| per-host memory: unavailable |
| tuple ids: 2 |
| |
| PLAN FRAGMENT 1 |
| PARTITION: RANDOM |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 5 |
| UNPARTITIONED |
| |
| 3:AGGREGATE |
| | output: COUNT(*) |
| | cardinality: 1 |
| | per-host memory: 10.00MB |
| | tuple ids: 2 |
| | |
| 2:HASH JOIN |
| | join op: INNER JOIN (BROADCAST) |
| | hash predicates: |
| | big.id = small.id |
| | cardinality: 1000000000 |
| | per-host memory: 3.15MB |
| | tuple ids: 1 0 |
| | |
| |----4:EXCHANGE |
| | cardinality: 1000000 |
| | per-host memory: 0B |
| | tuple ids: 0 |
| | |
| 1:SCAN HDFS |
| table=join_order.big #partitions=1/1 size=23.12GB |
| table stats: 1000000000 rows total |
| column stats: all |
| cardinality: 1000000000 |
| per-host memory: 88.00MB |
| tuple ids: 1 |
| |
| PLAN FRAGMENT 2 |
| PARTITION: RANDOM |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 4 |
| UNPARTITIONED |
| |
| 0:SCAN HDFS |
| table=join_order.small #partitions=1/1 size=17.93MB |
| table stats: 1000000 rows total |
| column stats: all |
| cardinality: 1000000 |
| per-host memory: 32.00MB |
| tuple ids: 0 |
+-----------------------------------------------------------+
Returned 64 row(s) in 0.03s
当类似这些的查询实际运行时,执行时间是相对固定的,不管查询语句中表的顺序如何。下面的例子使用了唯一的 ID 列和包含重复值的 VAL 列:
[localhost:21000] > select count(*) from big join small on (big.id = small.id);
Query: select count(*) from big join small on (big.id = small.id)
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
Returned 1 row(s) in 21.68s
[localhost:21000] > select count(*) from small join big on (big.id = small.id);
Query: select count(*) from small join big on (big.id = small.id)
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
Returned 1 row(s) in 20.45s
[localhost:21000] > select count(*) from big join small on (big.val = small.val);
+------------+
| count(*) |
+------------+
| 2000948962 |
+------------+
Returned 1 row(s) in 108.85s
[localhost:21000] > select count(*) from small join big on (big.val = small.val);
+------------+
| count(*) |
+------------+
| 2000948962 |
+------------+
Returned 1 row(s) in 100.76s
![13155539_J8dz.jpg](https://i-blog.csdnimg.cn/blog_migrate/dff65c7b8348f55de11c7808ebdeddb1.jpeg)
Impala 如何使用统计信息进行查询优化
当统计信息可用时,Impala 可以更好的优化复杂的或多表查询,可以更好地理解数据量和值的分布,并使用这些信息帮助查询并行处理和分布负载。下面的章节描述了 Impala 可以使用的统计信息的分类,以及如何产生这些信息并保持最新。
原来 Impala 依靠 Hive 采集统计信息的机制,通过 Hive ANALYZE TABLE 语句初始化一个 MapReduce 作业进行。为了更好的性能、用户友好性和可靠性, 在 1.2.1 之后,Impala 实现了自己的 COMPUTE STATS 语句,以及相关的 SHOW TABLE STATS 和 SHOW COLUMN STATS 语句。
表统计信息
当 metastore 数据库中的元数据可用时,Impala 查询计划器可以使用整个表和分区的统计信息。这些元数据用于本表的某些优化,并和列统计信息组合用于其他优化。
当向表或分区加载数据加载数据后,使用以下技术之一采集表的统计信息:
- 在 Impala 中执行 COMPUTE STATS 语句。这一在 Impala 1.2.2 新引入的语句是首选方法,因为:
- 它在单个操作中采集表、表的所有分区和所有列的统计信息
- 它不依赖于任意特殊的 Hive 设置、 metastore 配置、或单独的数据库来存放统计信息
- 它使用 Impala 查询基础架构来计算行数、不同值个数等等,通常比用 Hive ANALYZE TABLE statement.
- 当 Hive 中设置 hive.stats.autogather 为启用时,通过 Hive INSERT OVERWRITE 语句加载数据
- 为整个表或特定分区在 Hive 中执行 ANALYZE TABLE 语句:
例如,为非分区表采集统计信息:ANALYZE TABLEtablename[PARTITION(partcol1[=val1],partcol2[=val2], ...)] COMPUTE STATISTICS [NOSCAN];
为以 state 和 city 分区列的分区表 store 表采集所有分区的统计信息:ANALYZE TABLE customer COMPUTE STATISTICS;
只采集分区表 store 中 California 分区的统计信息:ANALYZE TABLE store PARTITION(s_state, s_county) COMPUTE STATISTICS;
ANALYZE TABLE store PARTITION(s_state='CA', s_county) COMPUTE STATISTICS;
使用 SHOW TABLE STATS table_name 语句,查看表的统计信息是否可用,以及统计信息的详细内容。参考 SHOW Statement 了解详细信息。
假如你使用基于 Hive 的方法采集统计信息,参见 the Hive wiki 了解关于 Hive 的配置要求。 Cloudera 推荐使用 Impala COMPUTE STATS 语句以避免 Hive 采集统计信息程序潜在的配置和可扩展性方面的问题。
列统计信息
当 metastore 数据库中的元数据可用时,Impala 查询计划器可以使用单个列的统计信息。这一技术对于比较连接查询中所有表的连接列,以帮助评估查询中每一个表将返回多少行最有价值。目前 Impala 自身不会自动创建这些元数据。使用 Hive 中的 ANALYZE TABLE 语句收集这些统计信息(无论表是在 Impala 还是 Hive 中创建的,这一语句都可以正常工作)。
![13155539_J8dz.jpg](https://i-blog.csdnimg.cn/blog_migrate/dff65c7b8348f55de11c7808ebdeddb1.jpeg)
对于特定的一组列,使用 SHOW COLUMN STATS table_name 语句检查列统计信息是否可用,或检查针对引用这系列的表的查询的扩展的 EXPLAIN 输出。参见 SHOW 语句 和 EXPLAIN 语句了解详细信息。
通过 ALTER TABLE 手工设置统计信息
所有统计信息中最关键的部分是表(未分区的表)或分区(分区表)中的行数。COMPUTE STATS 语句总是采集所有列的统计信息以及整个表的统计信息。假如在添加了一个分区或插入数据之后,进行完整的 COMPUTE STATS 操作实际不可行时,或者当行数不同时,可以预见 Impala 将产生更好的执行计划时,你可以通过 ALTER TABLE 语句手工设置行数:
create table analysis_data stored as parquet as select * from raw_data;
Inserted 1000000000 rows in 181.98s
compute stats analysis_data;
insert into analysis_data select * from smaller_table_we_forgot_before;
Inserted 1000000 rows in 15.32s
-- 现在表里共有 1001000000 行。我们可以更新统计信息中的这一个数据点
alter table analysis_data set tblproperties('numRows'='1001000000');
对于分区表,同时更新每一个分区的行数和整个表的行数:
-- 如果原来表中包含 1000000 行,我们新添加了一个分区
-- 修改该分区和整个表的 numRows 属性
alter table partitioned_data partition(year=2009, month=4) set tblproperties ('numRows'='30000');
alter table partitioned_data set tblproperties ('numRows'='1030000');
实际上,COMPUTE STATS 语句已经够快了,这一技术是不必要的。这一方法最大的价值就是可以调整 numRows 值的大小来产生理想的连接顺序从而解决性能问题(It is most useful as a workaround for in case of performance issues where you might adjust the numRowsvalue higher or lower to produce the ideal join order)。
在 Impala 使用表和列统计信息的例子
下面的例子通过一系列的 SHOW TABLE STATS, SHOW COLUMN STATS, ALTER TABLE, SELECT , INSERT 语句来演示了 Impala 如何使用统计信息帮助优化查询的各个方面。
这一例子展示了 STORE 表的表和列的统计信息,这个表使用的是 TPC-DS 决策支持系统基准测试中的表。这是一个只有 12 行数据的小表。最初,在使用 COMPUTE STATS 采集统计信息之前,大多数数字列显示占位符 -1,表示这一数字是未知的。这一待填充的数值是容易在物理层计量或推断出的,如文件个数,文件的总数据大小,以及对具有固定大小如 INT,FLOAT,TIMESTAMP 等数据类型的最大和平均大小(The figures that are filled in are values that are easily countable or deducible at the physical level, such as the number of files, total data size of the files, and the maximum and average sizes for data types that have a constant size such as INT, FLOAT, and TIMESTAMP)。
[localhost:21000] > show table stats store;
+-------+--------+--------+--------+
| #Rows | #Files | Size | Format |
+-------+--------+--------+--------+
| -1 | 1 | 3.08KB | TEXT |
+-------+--------+--------+--------+
Returned 1 row(s) in 0.03s
[localhost:21000] > show column stats store;
+--------------------+-----------+------------------+--------+----------+----------+
| Column | Type | #Distinct Values | #Nulls | Max Size | Avg Size |
+--------------------+-----------+------------------+--------+----------+----------+
| s_store_sk | INT | -1 | -1 | 4 | 4 |
| s_store_id | STRING | -1 | -1 | -1 | -1 |
| s_rec_start_date | TIMESTAMP | -1 | -1 | 16 | 16 |
| s_rec_end_date | TIMESTAMP | -1 | -1 | 16 | 16 |
| s_closed_date_sk | INT | -1 | -1 | 4 | 4 |
| s_store_name | STRING | -1 | -1 | -1 | -1 |
| s_number_employees | INT | -1 | -1 | 4 | 4 |
| s_floor_space | INT | -1 | -1 | 4 | 4 |
| s_hours | STRING | -1 | -1 | -1 | -1 |
| s_manager | STRING | -1 | -1 | -1 | -1 |
| s_market_id | INT | -1 | -1 | 4 | 4 |
| s_geography_class | STRING | -1 | -1 | -1 | -1 |
| s_market_desc | STRING | -1 | -1 | -1 | -1 |
| s_market_manager | STRING | -1 | -1 | -1 | -1 |
| s_division_id | INT | -1 | -1 | 4 | 4 |
| s_division_name | STRING | -1 | -1 | -1 | -1 |
| s_company_id | INT | -1 | -1 | 4 | 4 |
| s_company_name | STRING | -1 | -1 | -1 | -1 |
| s_street_number | STRING | -1 | -1 | -1 | -1 |
| s_street_name | STRING | -1 | -1 | -1 | -1 |
| s_street_type | STRING | -1 | -1 | -1 | -1 |
| s_suite_number | STRING | -1 | -1 | -1 | -1 |
| s_city | STRING | -1 | -1 | -1 | -1 |
| s_county | STRING | -1 | -1 | -1 | -1 |
| s_state | STRING | -1 | -1 | -1 | -1 |
| s_zip | STRING | -1 | -1 | -1 | -1 |
| s_country | STRING | -1 | -1 | -1 | -1 |
| s_gmt_offset | FLOAT | -1 | -1 | 4 | 4 |
| s_tax_precentage | FLOAT | -1 | -1 | 4 | 4 |
+--------------------+-----------+------------------+--------+----------+----------+
Returned 29 row(s) in 0.04s
使用 Hive ANALYZE TABLE 语句采集列的统计信息,你必须指定要采集统计信息的每一个列。而 Impala COMPUTE STATS 语句自动采集所有列的统计信息,因为它较快的读取整个表并高效的计算所有列的值。下面例子展示了执行 COMPUTE STATS 语句之后,表和所有列的统计信息都被填充:
[localhost:21000] > compute stats store;
+------------------------------------------+
| summary |
+------------------------------------------+
| Updated 1 partition(s) and 29 column(s). |
+------------------------------------------+
Returned 1 row(s) in 1.88s
[localhost:21000] > show table stats store;
+-------+--------+--------+--------+
| #Rows | #Files | Size | Format |
+-------+--------+--------+--------+
| 12 | 1 | 3.08KB | TEXT |
+-------+--------+--------+--------+
Returned 1 row(s) in 0.02s
[localhost:21000] > show column stats store;
+--------------------+-----------+------------------+--------+----------+-------------------+
| Column | Type | #Distinct Values | #Nulls | Max Size | Avg Size |
+--------------------+-----------+------------------+--------+----------+-------------------+
| s_store_sk | INT | 12 | 0 | 4 | 4 |
| s_store_id | STRING | 6 | 0 | 16 | 16 |
| s_rec_start_date | TIMESTAMP | 4 | 0 | 16 | 16 |
| s_rec_end_date | TIMESTAMP | 3 | 6 | 16 | 16 |
| s_closed_date_sk | INT | 3 | 9 | 4 | 4 |
| s_store_name | STRING | 8 | 0 | 5 | 4.25 |
| s_number_employees | INT | 9 | 0 | 4 | 4 |
| s_floor_space | INT | 10 | 0 | 4 | 4 |
| s_hours | STRING | 2 | 0 | 8 | 7.083300113677979 |
| s_manager | STRING | 7 | 0 | 15 | 12 |
| s_market_id | INT | 7 | 0 | 4 | 4 |
| s_geography_class | STRING | 1 | 0 | 7 | 7 |
| s_market_desc | STRING | 10 | 0 | 94 | 55.5 |
| s_market_manager | STRING | 7 | 0 | 16 | 14 |
| s_division_id | INT | 1 | 0 | 4 | 4 |
| s_division_name | STRING | 1 | 0 | 7 | 7 |
| s_company_id | INT | 1 | 0 | 4 | 4 |
| s_company_name | STRING | 1 | 0 | 7 | 7 |
| s_street_number | STRING | 9 | 0 | 3 | 2.833300113677979 |
| s_street_name | STRING | 12 | 0 | 11 | 6.583300113677979 |
| s_street_type | STRING | 8 | 0 | 9 | 4.833300113677979 |
| s_suite_number | STRING | 11 | 0 | 9 | 8.25 |
| s_city | STRING | 2 | 0 | 8 | 6.5 |
| s_county | STRING | 1 | 0 | 17 | 17 |
| s_state | STRING | 1 | 0 | 2 | 2 |
| s_zip | STRING | 2 | 0 | 5 | 5 |
| s_country | STRING | 1 | 0 | 13 | 13 |
| s_gmt_offset | FLOAT | 1 | 0 | 4 | 4 |
| s_tax_precentage | FLOAT | 5 | 0 | 4 | 4 |
+--------------------+-----------+------------------+--------+----------+-------------------+
Returned 29 row(s) in 0.04s
下面的例子展示了分区表中统计信息如何表示。这时,我们设置了一个存放世界上最琐碎的户籍数据的表,包含一个 STRING 字段,根据 YEAR 列进行分区。表统计信息中每一个分区都包含一个单独的实体,再加上最终的总数。对于分区列,列统计信息中包含一些容易推断的事实,如不同值的个数(分区子目录的个数) 和 NULL 值的个数(分区列中不可能出现)。
localhost:21000] > describe census;
+------+----------+---------+
| name | type | comment |
+------+----------+---------+
| name | string | |
| year | smallint | |
+------+----------+---------+
Returned 2 row(s) in 0.02s
[localhost:21000] > show table stats census;
+-------+-------+--------+------+---------+
| year | #Rows | #Files | Size | Format |
+-------+-------+--------+------+---------+
| 2000 | -1 | 0 | 0B | TEXT |
| 2004 | -1 | 0 | 0B | TEXT |
| 2008 | -1 | 0 | 0B | TEXT |
| 2010 | -1 | 0 | 0B | TEXT |
| 2011 | 0 | 1 | 22B | TEXT |
| 2012 | -1 | 1 | 22B | TEXT |
| 2013 | -1 | 1 | 231B | PARQUET |
| Total | 0 | 3 | 275B | |
+-------+-------+--------+------+---------+
Returned 8 row(s) in 0.02s
[localhost:21000] > show column stats census;
+--------+----------+------------------+--------+----------+----------+
| Column | Type | #Distinct Values | #Nulls | Max Size | Avg Size |
+--------+----------+------------------+--------+----------+----------+
| name | STRING | -1 | -1 | -1 | -1 |
| year | SMALLINT | 7 | 0 | 2 | 2 |
+--------+----------+------------------+--------+----------+----------+
Returned 2 row(s) in 0.02s
下面的例子演示了在 Impala 中执行 COMPUTE STATS 语句后统计信息是如何填充的。
[localhost:21000] > compute stats census;
+-----------------------------------------+
| summary |
+-----------------------------------------+
| Updated 3 partition(s) and 1 column(s). |
+-----------------------------------------+
Returned 1 row(s) in 2.16s
[localhost:21000] > show table stats census;
+-------+-------+--------+------+---------+
| year | #Rows | #Files | Size | Format |
+-------+-------+--------+------+---------+
| 2000 | -1 | 0 | 0B | TEXT |
| 2004 | -1 | 0 | 0B | TEXT |
| 2008 | -1 | 0 | 0B | TEXT |
| 2010 | -1 | 0 | 0B | TEXT |
| 2011 | 4 | 1 | 22B | TEXT |
| 2012 | 4 | 1 | 22B | TEXT |
| 2013 | 1 | 1 | 231B | PARQUET |
| Total | 9 | 3 | 275B | |
+-------+-------+--------+------+---------+
Returned 8 row(s) in 0.02s
[localhost:21000] > show column stats census;
+--------+----------+------------------+--------+----------+----------+
| Column | Type | #Distinct Values | #Nulls | Max Size | Avg Size |
+--------+----------+------------------+--------+----------+----------+
| name | STRING | 4 | 1 | 5 | 4.5 |
| year | SMALLINT | 7 | 0 | 2 | 2 |
+--------+----------+------------------+--------+----------+----------+
Returned 2 row(s) in 0.02s
关于在统计信息可用时,演示一些查询工作方式不同的例子,参见 Examples of Join Order Optimization。在采集统计信息之前和之后,观察 EXPLAIN 的输出,你可以看到 Impala 使用不同方式执行同一个查询。对比之前和之后的查询时间,检查之前和之后 PROFILE 输出的吞吐量的值, to verify how much the improved plan speeds up performance.
Impala 查询基准测试
与其他 Hadoop 组件类似,因为 Impala 是设计用来处理分布式环境中大量数据的,所以应使用真实的数据和集群配置进行性能测试。使用多节点的集群而不是单节点的;对包含 TB 数据的表进行查询而不是几十G的。Impala 所有使用的并行处理技术最适合用于超出了单个服务器容量的负载。
当你执行查询返回大量的行,打印输出结果所花费的 CPU 时间是巨大的,为实际查询时间添加了不准确的度量(the CPU time to pretty-print the output can be substantial, giving an inaccurate measurement of the actual query time)。请考虑在 impala-设立了 命令中使用 -B 选项关闭打印结果,而可选的 -o 选项可以保存查询结果到一个文件而不是打印到屏幕上。参见 impala-shell Command-Line Options 了解详细信息。
控制资源使用
通过为 impalad 守护进程指定 -mem_limits 选项,你可以限制查询执行时 Impala 使用的内存量。参见 Modifying Impala Startup Options 了解详细信息。这一限制仅对查询直接消耗的内存有效;Impala 在启动时保留了额外的内存,例如用于缓存元数据。
对于生产部署,Cloudera 推荐使用如 cgroups 机制实现资源隔离,可以在 Cloudera Manager 中配置。参见 Managing Clusters with Cloudera Manager 了解详细信息。
当你结合 CDH 5 使用 Impala 时,你可以像在 Using Resource Management with Impala (CDH 5 Only) 中描述的那样使用 YARN 资源管理框架。目前 CDH 5 仍是 beta 版;用于 CDH 5 beta 版的对应 Impala 版本是 1.2.0。
理解 EXPLAIN 计划
EXPLAIN 语句提供查询将要执行的逻辑步骤的大纲,例如工作在节点之间如何分布,以及中间结果如何组合产生最终结果集。你可以在实际执行查询之前看到这些详细信息。你可以使用这些信息来检查查询是否使用一些非常意外的或低效的方式执行。
在查询 profile 报告的开始部分,EXPLAIN 计划同样被打印出来,以便于检查查询的逻辑和物理的各个方面。
EXPLAIN 输出的细节的数量由 EXPLAIN_LEVEL 查询选项控制。当性能调整时复核表和列的统计信息时,或与 CDH5 中资源管理功能联合评估查询资源使用情况时(or when estimating query resource usage in conjunction with the resource management features in CDH 5),通常从 normal 修改为 verbose (或 0 到 1)。
理解查询 Profile
PROFILE 语句在 impala-shell 中可用,产生一个最近执行语句的详细的底层报告。 不像在 Understanding the EXPLAIN Plan 中描述的 EXPLAIN 那样,这一信息仅当查询执行完成后可用。它展示了物理细节如每一节点读取的字节数,最大内存使用等等信息。你可以使用这些信息确定查询是 I/O 密集(I/O-bound)还是 CPU 密集(CPU-bound),是否一些网络条件达到瓶颈,是否一台放缓影响到了部分节点而不影响另一部分(whether a slowdown is affecting some nodes but not others),并检查推荐配置如 short-circuit local reads 是否生效。
EXPLAIN plan 同样被打印在查询 profile 报告的开始,以便于检查查询的逻辑和物理的各个方面。在 EXPLAIN_LEVEL 中描述的 EXPLAIN_LEVEL 查询选项,同样对控制 PROFILE 命令中产生的 EXPLAIN 输出打印的详细程度有效。
测试 Impala 性能
测试以确保 Impala 为性能进行了最优配置。假如你没有使用 Cloudera Manager 安装的 Impala,完成本主题中描述的内容以帮助确认已经合适的配置。即使你使用 Cloudera Manager 安装的 Impala,已经自动应用合适的配置,这一过程可以检验 Impala 设置是否正确。
检查 Impala 配置值
你可以使用浏览器连接到 Impala 服务器检查 Impala 的配置值:
检查 Impala 配置值:
- 使用浏览器连接到你的环境中运行 impalad 进程的主机之一。使用类似格式连接 http://hostname:port/varz
Note: 在前面的例子中,替换 hostname 和 port 为你的 Impala 的名称和端口。默认端口是 25000
- 查看已配置的值
例如,检查你的系统是否启用了本地块跟踪信息(block locality tracking information),应检查 dfs.datanode.hdfs-blocks-metadata.enabled 的值是否为 true
检查数据本地化(data locality):
- 在多个节点上都可用的数据集上执行查询。例如,对具有合理机会传播到多个数据节点上表 MyTable 进行查询:
[impalad-host:21000] > SELECT COUNT (*) FROM MyTable
- 当查询完成后,检查 Impala 日志的内存。你可能会发现类似下面的消息:
Total remote scan volume = 0
远程扫描的存在标识 impalad 没有运行在正确的节点上。当一些数据节点上没有运行 impalad 或无法运行,因为启动查询的 impalad 实例无法连接到一个或多个 impalad 实例(This can be because some DataNodes do not have impalad running or it can be because the impalad instance that is starting the query is unable to contact one or more of the impalad instances)。
理解这些问题的原因:
- 连接到调试web服务器。默认的,服务器运行在 25000 端口。这一页面列出了你集群中所有在运行的 impalad 实例。假如列出的实例少于你的预期,这通常表明一些 DataNode 没有运行 impalad。请确保所有 DataNode 都启动了 impalad
- 假如你使用多宿主(multi-homed)主机,请确保 Impala 守护进程的主机名解析到运行的 impalad(If you are using multi-homed hosts, ensure that the Impala daemon's hostname resolves to the interface on which impalad is running)。Impala 在启动 impalad 时显示主机名。假如需要明确设置主机名,请使用 --hostname 标志
- 检查 statestored 是否正常运行。复查 state store 日志的内容以确保所有的 impalad 实例别列为连接到 state store
复查 Impala 日志
你可以复查 Impala 日志的内容,查找短路读取(short-circuit reads)或块本地跟踪(block location tracking)没有正常运行的标志。在检查日志之前,对一个小的 HDFS 数据集执行一个简单的查询。完成一个查询任务使用当前设置产生日志信息。启动 Impala 和执行查询的信息可以在 Starting Impala 和 Using the Impala Shell 找到。登录信息可以在 Using Impala Logging 中找到。日志信息和对应的描述如下:
Log Message | Interpretation |
---|---|
| Tracking block locality 未启用 |
| Native checksumming 未启用 |