我正在测试一个相当累人的程序 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条记录,然后执行查询,但这似乎有些草率。另外,那之后我还要清理临时table。这种行为看起来像Hive虫,即 limit
在这种情况下,行为显然不像看上去那样。
2条答案
按热度按时间yzckvree1#
这个
limit
在case
,但在处理case
-它实际上被应用了两次。虽然这是巧合,但在本例中,limit的两个应用程序分别对应于内部查询和外部查询。在查询计划中,您可以看到Map阶段只选择一个列(“
expressions: domainname
)并将结果数减少到10个(从157462377267)。这与内部查询相对应。然后reduce阶段应用于案例(“expressions: CASE WHEN ((_col0 rlike '.*:443$')) THEN (1) ELSE (0) END
)并将行数减少到10,但是您可以看到,在这个阶段中,预期的输入行数已经是10。reduce阶段对应于外部查询。限制应用两次的原因是分布式执行。因为在map阶段结束时,您希望最小化发送到reducer的数据量,所以在这里应用限制是有意义的。达到限制后,Map程序将不再处理任何输入。然而,这还不够,因为每个Map器可能产生多达10个结果,加起来是Map器数量的10倍,因此reduce阶段必须再次应用限制。由于这种机制,通常您应该直接应用限制,而不是仅为此目的创建子查询。
总而言之,在我的解释中,查询计划看起来不错
limit
在应该处理的地方处理。这回答了你的问题,为什么limit
在case
. 遗憾的是,这并不能解释为什么要花这么多时间。更新:请参阅ozw1z5rd关于为什么这个查询尽管使用了
limit
. 它解释了使用子查询会导致mapreduce作业启动,而直接查询会避免这种情况。yacmzcpb2#
请看这些例子:
28秒后完成,Map缩小作业已启动。
0.162秒,并且没有启动任何map reduce作业。
一旦您输入到子查询中,map reduce作业就开始选择您需要的行。
在mapreduce中,输入文件被拆分,并且对于每个拆分,都会启动一个Map器。每个Map器只需获取数据并对其进行转换,然后将其传递给组合器、分区器和缩减器。因为每个Map器都是自己工作的,所以无法知道处理了多少行。除非使用分区表或临时表,否则这将强制执行表扫描。如您所见,限制是在计划中的表扫描之后完成的:
这工作很慢
这工作很快
还要注意limit返回随机列:here。