HIVE高级(10):优化(10) HQL 语法优化(1) 单表优化

1 列裁剪与分区裁剪

  列裁剪就是在查询时只读取需要的列,分区裁剪就是只读取需要的分区。当列很多或者数据量很大时,如果 select * 或者不指定分区,全列扫描和全表扫描效率都很低。
  Hive 在读数据的时候,可以只读取查询中所需要用到的列,而忽略其他的列。这样做可以节省读取开销:中间表存储开销和数据整合开销。

2 Group By

  默认情况下,Map 阶段同一 Key 数据分发给一个 Reduce,当一个 key 数据过大时就倾斜了。
并不是所有的聚合操作都需要在 Reduce 端完成,很多聚合操作都可以先在 Map 端进行部分聚合,最后在 Reduce 端得出最终结果。 
开启 Map 端聚合参数设置
(1)是否在 Map 端进行聚合,默认为 True
set hive.map.aggr = true;
(2)在 Map 端进行聚合操作的条目数目
set hive.groupby.mapaggr.checkinterval = 100000;
(3)有数据倾斜的时候进行负载均衡(默认是 false)
set hive.groupby.skewindata = true;
当选项设定为 true,生成的查询计划会有两个 MR Job。 
  第一个 MR Job 中,Map 的输出结果会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce中,从而达到负载均衡的目的;
  第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作(虽然
能解决数据倾斜,但是不能让运行速度的更快)。
hive (default)> select deptno from emp group by deptno;
Stage-Stage-1: Map: 1 Reduce: 5 Cumulative CPU: 23.68 sec HDFS Read: 
19987 HDFS Write: 9 SUCCESS
Total MapReduce CPU Time Spent: 23 seconds 680 msec
OK
deptno
10
20
30
优化以后 
hive (default)> set hive.groupby.skewindata = true;
hive (default)> select deptno from emp group by deptno;
Stage-Stage-1: Map: 1 Reduce: 5 Cumulative CPU: 28.53 sec HDFS Read: 
18209 HDFS Write: 534 SUCCESS
Stage-Stage-2: Map: 1 Reduce: 5 Cumulative CPU: 38.32 sec HDFS Read: 
15014 HDFS Write: 9 SUCCESS
Total MapReduce CPU Time Spent: 1 minutes 6 seconds 850 msec
OK
deptno
10
20
30

3 Vectorization

vectorization : 矢量计算的技术,在计算类似scan, filter, aggregation的时候,vectorization技术以设置批处理的增量大小为 1024 行单次来达到比单条记录单次获得更高的效率。
set hive.vectorized.execution.enabled = true;
set hive.vectorized.execution.reduce.enabled = true;

4 Count(Distinct) 去重统计

  数据量小的时候无所谓,数据量大的情况下,由于 COUNT DISTINCT 操作需要用一个Reduce Task 来完成,这一个 Reduce 需要处理的数据量太大,就会导致整个 Job 很难完成,
一般 COUNT DISTINCT 使用先 GROUP BY 再 COUNT 的方式替换,但是需要注意 group by 造成的数据倾斜问题.
1)案例实操
(1)创建一张大表
hive (default)> create table bigtable(id bigint, time bigint, uid string, 
keyword
string, url_rank int, click_num int, click_url string) row format 
delimited
fields terminated by '	';
(2)加载数据 
hive (default)> load data local inpath '/opt/module/data/bigtable' into table bigtable;
(3)设置 5 个 reduce 个数
set mapreduce.job.reduces = 5;
(4)执行去重 id 查询 
复制代码
hive (default)> select count(distinct id) from bigtable;
Stage-Stage-1: Map: 1 Reduce: 1 Cumulative CPU: 7.12 sec HDFS Read: 
120741990 HDFS Write: 7 SUCCESS
Total MapReduce CPU Time Spent: 7 seconds 120 msec
OK
c0
100001
Time taken: 23.607 seconds, Fetched: 1 row(s)
复制代码
(5)采用 GROUP by 去重 id
复制代码
hive (default)> select count(id) from (select id from bigtable group by 
id) a;
Stage-Stage-1: Map: 1 Reduce: 5 Cumulative CPU: 17.53 sec HDFS Read: 
120752703 HDFS Write: 580 SUCCESS
Stage-Stage-2: Map: 1 Reduce: 1 Cumulative CPU: 4.29 sec2 HDFS Read: 
9409 HDFS Write: 7 SUCCESS
Total MapReduce CPU Time Spent: 21 seconds 820 msec
OK
_c0
100001
Time taken: 50.795 seconds, Fetched: 1 row(s)
复制代码
虽然会多用一个 Job 来完成,但在数据量大的情况下,这个绝对是值得的。

5 多重模式

如果你碰到一堆 SQL,并且这一堆 SQL 的模式还一样。都是从同一个表进行扫描,做不同的逻辑。有可优化的地方:如果有 n 条 SQL,每个 SQL 执行都会扫描一次这张表。 
insert .... select id,name,sex, age from student where age > 17;
insert .... select id,name,sex, age from student where age > 18;
insert .... select id,name,sex, age from student where age > 19;
-- 隐藏了一个问题:这种类型的 SQL 有多少个,那么最终。这张表就被全表扫描了多少次
insert int t_ptn partition(city=A). select id,name,sex, age from student 
where city= A;
insert int t_ptn partition(city=B). select id,name,sex, age from student 
where city= B;
insert int t_ptn partition(city=c). select id,name,sex, age from student 
where city= c;

修改为:
from student
insert int t_ptn partition(city=A) select id,name,sex, age where city= A
insert int t_ptn partition(city=B) select id,name,sex, age where city= B
如果一个 HQL 底层要执行 10 个 Job,那么能优化成 8 个一般来说,肯定能有所提高,多重插入就是一个非常实用的技能。一次读取,多次插入,有些场景是从一张表读取数据后,要多次利用。

6 in/exists 语句

在 Hive 的早期版本中,in/exists 语法是不被支持的,但是从 hive-0.8x 以后就开始支持这个语法。但是不推荐使用这个语法。虽然经过测验,Hive-2.3.6 也支持 in/exists 操作,但还是推荐使用 Hive 的一个高效替代方案:left semi join 
比如说:-- in / exists 实现
select a.id, a.name from a where a.id in (select b.id from b);
select a.id, a.name from a where exists (select id from b where a.id = 
b.id);
可以使用 join 来改写:
select a.id, a.name from a join b on a.id = b.id;
应该转换成:
-- left semi join 实现
select a.id, a.name from a left semi join b on a.id = b.id;

本文来自博客园,作者:秋华,转载请注明原文链接:https://www.cnblogs.com/qiu-hua/p/15143780.html

原文地址:https://www.cnblogs.com/qiu-hua/p/15143780.html