我正在hive查询中测试一个相当繁重的rlike
函数。我想在将其应用到我的TB +数据之前先对子集进行测试。 完整查询为:
create table proxy_parsed_clean as
select
a.*,
case
when domainname rlike '.*:443$' then 1
else 0
end as used_https
from proxy_parsed a;
由于有如此多的数据,我写了一个(表面上)对一个子集进行操作的查询:
select
case
when a.domainname rlike '.*:443$' then 1
else 0
end as used_https
from (select domainname from proxy_parsed limit 10) a;
但是,只要第一个查询,这似乎只需要 。它不是将外部查询应用于子集,而是将case语句应用于整个数据集,然后然后限制。运行explain
确认了我的怀疑(注意limit
子句已移至查询结尾):
> explain select case when a.domainname rlike '.*:443$' then 1 else 0 end from (select domainname from proxy_parsed limit 10) a;
+---------------------------------------------------------------------------------------------------------------------+--+
| Explain |
+---------------------------------------------------------------------------------------------------------------------+--+
| STAGE DEPENDENCIES: |
| Stage-1 is a root stage |
| Stage-0 depends on stages: Stage-1 |
| |
| STAGE PLANS: |
| Stage: Stage-1 |
| Map Reduce |
| Map Operator Tree: |
| TableScan |
| alias: proxy_parsed |
| Statistics: Num rows: 157462377267 Data size: 6298495090688 Basic stats: COMPLETE Column stats: NONE |
| Select Operator |
| expressions: domainname (type: varchar(40)) |
| outputColumnNames: _col0 |
| Statistics: Num rows: 157462377267 Data size: 6298495090688 Basic stats: COMPLETE Column stats: NONE |
| Limit |
| Number of rows: 10 |
| Statistics: Num rows: 10 Data size: 400 Basic stats: COMPLETE Column stats: NONE |
| Reduce Output Operator |
| sort order: |
| Statistics: Num rows: 10 Data size: 400 Basic stats: COMPLETE Column stats: NONE |
| TopN Hash Memory Usage: 0.1 |
| value expressions: _col0 (type: varchar(40)) |
| Reduce Operator Tree: |
| Select Operator |
| expressions: VALUE._col0 (type: varchar(40)) |
| outputColumnNames: _col0 |
| Statistics: Num rows: 10 Data size: 400 Basic stats: COMPLETE Column stats: NONE |
| Limit |
| Number of rows: 10 |
| Statistics: Num rows: 10 Data size: 400 Basic stats: COMPLETE Column stats: NONE |
| Select Operator |
| expressions: CASE WHEN ((_col0 rlike '.*:443$')) THEN (1) ELSE (0) END (type: int) |
| outputColumnNames: _col0 |
| Statistics: Num rows: 10 Data size: 400 Basic stats: COMPLETE Column stats: NONE |
| File Output Operator |
| compressed: false |
| Statistics: Num rows: 10 Data size: 400 Basic stats: COMPLETE Column stats: NONE |
| table: |
| input format: org.apache.hadoop.mapred.TextInputFormat |
| output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat |
| serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe |
| |
| Stage: Stage-0 |
| Fetch Operator |
| limit: -1 |
| Processor Tree: |
| ListSink |
| |
+---------------------------------------------------------------------------------------------------------------------+--+
如果我只是运行select * from proxy_parsed limit 10;
,则查询会快速执行。有人可以解释 A),为什么查询没有在子集上执行, B)如何制作呢?
我可以创建一个临时表,在其中选择10条记录,然后执行查询,但这看起来很草率。另外,我之后还有临时表来清理。这种行为看起来像是一个Hive bug,即limit
行为显然不像在这种情况下应该的那样。
答案 0 :(得分:3)
在limit
之后,case
未应用,但在处理case
之前和期间,它实际上已应用两次。虽然这是巧合,但在这种情况下,限制的两个应用程序分别对应于内部和外部查询。
在查询计划中,您可以看到Map阶段只选择一个列(“expressions: domainname
”),并且还将结果数减少到10(来自157462377267)。这对应于内部查询。然后,Reduce阶段应用大小写(“expressions: CASE WHEN ((_col0 rlike '.*:443$')) THEN (1) ELSE (0) END
”)并将行数减少到10,但是您可以看到此阶段的预期输入行数已经是10。 Reduce阶段对应于外部查询。
两次应用限制的原因是分布式执行。由于在Map阶段结束时您希望最小化发送到Reducers的数据量,因此在此处应用限制是有意义的。达到限制后,Mapper将不再处理任何输入。然而,这还不够,因为可能每个Mapper可能产生多达10个结果,加起来是Mapper数量的十倍,因此Reduce阶段必须再次应用限制。由于这种机制,通常您应该直接应用限制,而不是为此唯一目的创建子查询。
总而言之,在我的解释中,查询计划看起来很好 - limit
在它应该的位置处理。这回答了有关limit
在case
之前应用的原因的问题。可悲的是,它并没有解释为什么它需要这么多时间。
更新:请参阅ozw1z5rd's answer,了解尽管使用limit
此查询速度缓慢的原因。它解释了使用子查询可以启动MapReduce作业,而直接查询可以避免这种情况。
答案 1 :(得分:3)
请看这些例子:
hive> select case when d rlike '.*:443$' then 1 else 0 end as https from ( select d from domainname limit 5 ) a;
Query ID = hduser_20160908161152_e0be6db7-f5ac-40ee-b7fb-50ad58ca7f2f
Total jobs = 1
Launching Job 1 out of 1
:
.
OK
0
0
1
1
0
Time taken: 28.263 seconds, Fetched: 5 row(s)
完成28秒并开始减少地图工作。
hive> select case when d rlike '.*:443$' then 1 else 0 end as https from domainname limit 5;
OK
0
0
1
1
0
Time taken: 0.162 seconds, Fetched: 5 row(s)
0.162秒,并且没有启动地图缩减作业。
进入子查询后,将启动map reduce作业以选择所需的行。
在mapReduce中,输入文件被分割,并且对于每个分割,启动映射器。每个映射器只需获取数据并对其进行转换,然后将其传递给组合器,然后是分区器,然后是reducer。 由于每个映射器都是自己工作的,因此无法知道已经处理了多少行。这会强制进行表扫描,除非您使用分区/分区表或临时表。 如您所见,限制是在计划中的表扫描后完成的:
From Map Operator Tree:
1st does a full table scan ( Statistics: Num rows: 157462377267 )
2nd the operator limit will take only the first 10 rows.
Reducers will get these only these 10 rows from the Mappers as you can see from the reduce operator tree ( Statistics: Num rows: 10 )
这很慢
select
case
when a.domainname rlike '.*:443$' then 1
else 0
end as used_https
from (select domainname from proxy_parsed limit 10) a;
这很快就行了
select
case
when domainname rlike '.*:443$' then 1
else 0
end as used_https
from proxy_parsed limit 10;
还要注意限制返回随机列:here。