使用Impala在INSERT INTO(Parquet)TABLE期间对分区键进行排序

时间:2019-02-20 09:47:29

标签: sql hadoop parquet impala

我有一个ETL作业,我想将.csv文件中的数据附加到Impala表中。目前,我是通过使用新数据(.csv.lzo格式)创建一个临时外部.csv表,然后将其插入到主表中来实现的。

我使用的查询如下:

>>> BGPPathAttr.type_code.s2i['NEXT_HOP']
3
>>> BGPPathAttr.type_code.i2s[3]
'NEXT_HOP'

其中INSERT INTO TABLE main_table PARTITION(yr, mth) SELECT *, CAST(extract(ts, "year") AS SMALLINT) AS yr, CAST(extract(ts, "month") AS TINYINT) AS mth FROM csv_table 的定义如下(几列被截断):

main_table

数据约为几GB(5500万行,约30列),这需要一个多小时才能运行。我很好奇为什么会这样(因为对于本质上来说是一个追加操作的东西来说似乎很长),并在查询计划中遇到了这个问题:

CREATE TABLE IF NOT EXISTS main_table (
    tid             INT,
    s1              VARCHAR,
    s2              VARCHAR,
    status          TINYINT,
    ts              TIMESTAMP,
    n1              DOUBLE,
    n2              DOUBLE,
    p               DECIMAL(3,2),
    mins            SMALLINT,
    temp            DOUBLE
)
PARTITIONED BY (yr SMALLINT, mth TINYINT)
STORED AS PARQUET

显然,大部分时间和资源都花在了分区键上:

F01:PLAN FRAGMENT [HASH(CAST(extract(ts, 'year') AS SMALLINT),CAST(extract(ts, 'month') AS TINYINT))] hosts=2 instances=2
|  Per-Host Resources: mem-estimate=1.01GB mem-reservation=12.00MB thread-reservation=1
WRITE TO HDFS [default.main_table, OVERWRITE=false, PARTITION-KEYS=(CAST(extract(ts, 'year') AS SMALLINT),CAST(extract(ts, 'month') AS TINYINT))]
|  partitions=unavailable
|  mem-estimate=1.00GB mem-reservation=0B thread-reservation=0
|
02:SORT
|  order by: CAST(extract(ts, 'year') AS SMALLINT) ASC NULLS LAST, CAST(extract(ts, 'month') AS TINYINT) ASC NULLS LAST
|  materialized: CAST(extract(ts, 'year') AS SMALLINT), CAST(extract(ts, 'month') AS TINYINT)
|  mem-estimate=12.00MB mem-reservation=12.00MB spill-buffer=2.00MB thread-reservation=0
|  tuple-ids=1 row-size=1.29KB cardinality=unavailable
|  in pipelines: 02(GETNEXT), 00(OPEN)
|
01:EXCHANGE [HASH(CAST(extract(ts, 'year') AS SMALLINT),CAST(extract(ts, 'month') AS TINYINT))]
|  mem-estimate=2.57MB mem-reservation=0B thread-reservation=0
|  tuple-ids=0 row-size=1.28KB cardinality=unavailable
|  in pipelines: 00(GETNEXT)
|

为什么Impala必须这样做?有什么方法可以对表进行分区而不必对分区键进行排序,或者在我的情况下可以加快表的速度,我要附加的整个.csv文件只有一个或两个分区键?

编辑:事实证明这很可能是因为我使用的是Parquet文件格式。不过,我的问题仍然适用:当我知道实际上几乎不需要排序时,是否可以加快排序速度?

相比之下,Operator #Hosts Avg Time Max Time #Rows Est. #Rows Peak Mem Est. Peak Mem Detail ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 02:SORT 2 17m16s 30m50s 55.05M -1 25.60 GB 12.00 MB 01:EXCHANGE 2 9s493ms 12s822ms 55.05M -1 26.98 MB 2.90 MB HASH(CAST(extract(ts, 'year') AS SMALLINT),CAST(extract(ts, 'month') AS TINYINT)) 00:SCAN HDFS 2 51s958ms 1m10s 55.05M -1 76.06 MB 704.00 MB default.csv_table 之类的操作大约需要2-3分钟,而SELECT COUNT(*) FROM csv_table WHERE extract(ts, "year") = 2018 AND extract(ts, "month") = 1(在插入过程中完成)则需要一个多小时。此示例仅具有键(2018,1)和(2018,2)。

2 个答案:

答案 0 :(得分:0)

Impala进行排序是因为您使用动态分区。特别是对于具有oncomputed stats的表,impala在动态分区方面不太好。我建议您在动态分区的情况下使用配置单元。如果您不打算使用蜂巢,我的建议是:

  1. 在每次插入语句之前,都要在csv表上计算统计信息。
  2. 如果第一步不能正常工作,请对一些可能的分区使用静态分区,然后运行超出可能范围的动态分区。例如;如果有年份和月份的一种选择:
INSERT 
INTO TABLE main_table
PARTITION(yr=2019, mth=2)
SELECT
    *
FROM csv_table where CAST(extract(ts, "year") AS SMALLINT)=2019 and CAST(extract(ts, "month") AS TINYINT)=2;  
INSERT INTO TABLE main_table
PARTITION(yr, mth)
SELECT
    *,
    CAST(extract(ts, "year") AS SMALLINT),
    CAST(extract(ts, "month") AS TINYINT)
FROM csv_table where CAST(extract(ts, "year") AS SMALLINT)!=2019 and CAST(extract(ts, "month") AS TINYINT)!=2;

这些语句缩小了动态分区将要处理的集合。并有望减少花费的总时间。

答案 1 :(得分:0)

您可以添加提示以禁用排序阶段。

INSERT INTO TABLE main_table
PARTITION(yr, mth)  /* +NOCLUSTERED */
SELECT
    *,
    CAST(extract(ts, "year") AS SMALLINT) AS yr,
    CAST(extract(ts, "month") AS TINYINT) AS mth
FROM csv_table

如此处说明:Optimizer Hints

/ * +聚类 /和/ +无聚类 /提示/ +聚类 /排序 插入之前,请按分区列中的数据来确保只有一个 每个节点一次写入分区。使用此提示可减少 保持打开状态的文件数和内存中保留的缓冲区数 同时。此技术主要用于插入到 镶木表,其中大块块需要大量内存 一次缓冲多个输出文件的数据。这个提示是 在Impala 2.8或更高版本中可用。从Impala 3.0开始,/ + CLUSTERED * /是HDFS表的默认行为。

/ * + NOCLUSTERED * /在插入之前不按主键排序。这个 提示在Impala 2.8或更高版本中可用。在以下情况下使用此提示 插入Kudu表。

在低于Impala 3.0的版本中,/ * + NOCLUSTERED * /是 HDFS表中默认设置。