大家好,我是风云,欢迎大家关注我的个人原创公众号【笑看风云路】获取更多大数据技术干货,在未来的日子里我们一起来学习大数据相关的技术,一起努力奋斗,遇见更好的自己!
扫码进,更快捷
:
基于Hive的开发过程中主要涉及到的可能是SQL优化这块。
优化的核心思想是:
SELECT * (必须)
FROM 表(数据源)
WHERE 条件
GROUP BY 字段
HAVING 条件
ORDER BY 字段(排序 默认 ASC)
LIMIT 限制数据条数
第一步:确定数据源
FROM
JOIN
ON
第二步:过滤数据
WHERE
GROUP BY (开始使用SELECT 中的别名,后面的语句中都可以使用)
avg,sum.......
HAVING
第三步:查询数据
SELECT
第四步:显示数据
DISTINCT
ORDER BY
LIMIT
列裁剪:就是在查询时只读取需要的列。如果select * 或者不指定分区,导致全表扫描和全分区扫描效率都很低。配置项:hive.optimize.cp , 默认是true
分区裁剪:就是在查询时只读取需要的分区。配置项:hive.optimize.pruner,默认是true
例如:
select a.*
from a
left join b on a.uid = b.uid
where a.ds='2020-08-10'
and b.ds='2020-08-10'
- b表的过滤条件写在了where后面,会导致先全表关联,之后再过滤分区。而a表的过滤条件写在where后面是可以的,因为a表会进行谓词下推,就是先执行where,再执行join,但是b表不会进行谓词下推。
- on的条件没有过滤null值情况,如果两个数据表存在大批量null值情况,会造成数据倾斜。
优化1:
select a.*
from a
left join b on (a.uid is not null and a.uid = b.uid and b.ds='2020-08-10')
where a.ds='2020-08-10'
优化2:如果null也是需要的
select a.*
from a
left join b on (a.uid is not null and a.uid = b.uid and b.ds='2020-08-10')
where a.ds='2020-08-10'
union all
select a.* from a where a.uid is null
order by:将结果按某个字段全局排序,会导致所有map端数据都进入一个reducer中,当数据量大时可能会长时间计算不完。
sort by:就会根据情况启动多个reducer进行排序,并且保证每个reducer内局部有序。为了控制map端数据分配到reduce的key,往往还要配合distribute by一起使用。如果不加distribute by,map端数据就会随机分配给reducer。
-- 未优化写法
select a,b,c
from table
where xxx
order by a
limit 10;
-- 优化写法
select a,b,c
from table
where xxx
distribute by a
sort by a
limit 10;
例子1:
-- 取出user_trade表中全部支付用户
-- 原有写法
SELECT distinct user_name
FROM user_trade
WHERE dt>'0';
--测试时长 43 s
-- 优化写法
SELECT user_name
FROM user_trade
WHERE dt>'0'
GROUP BY user_name;
--测试时长 29 s
例子2:
-- 原有写法
select count(distinct uid)
from test
where ds='2020-08-10' and uid is not null
-- 优化写法
select count(a.uid)
from
(select uid
from test
where uid is not null and ds = '2020-08-10'
group by uid
) a
注意:COUNT DISTINCT操作需要用一个Reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,使用先GROUP BY再COUNT的方式替换,虽然会多用一个Job来完成,但在数据量大的情况下(且很多重复值),效率高于直接count(distinct)。
with as:是将语句中用到的子查询事先提取出来(类似临时表),使整个查询当中的所有模块都可以调用该查询结果,避免Hive对不同部分的相同子查询进行重复计算。
select a.*
from a
left join b on a.uid = b.uid
where a.ds='2020-08-10'
and b.ds='2020-08-10'
-- 转化为:
with test1 as
(
select uid
from b
where ds = '2020-08-10' and uid is not null
)
select a.*
from a
left join test1 on a.uid = test1.uid
where a.ds='2020-08-10' and a.uid is not null
grouping sets: 指定分组的维度,聚合结果均在同一列,分类字段用不同列来区分
例如:统计用户的性别分布,城市分布,等级分布
-- 通常使用三条sql语句实现
-- 1. 性别分布
select sex,
count(distinct user_id)
from user_info
group by sex;
-- 2. 城市分布
select city,
count(distinct user_id)
from user_info
group by city;
-- 3. 等级分布
select level,
count(distinct user_id)
from user_info
group by level;
优化后:
-- 使用 grouping sets 实现
select sex,city,level
count(distinct user_id)
from user_info
group by sex,city,level
grouping sets (sex,city,level)
cube:根据group by维度的所有组合进行聚合
-- 统计性别、城市、等级等维度的各种组合的用户分布
SELECT sex,
city,
level,
count(distinct user_id)
FROM user_info
GROUP BY sex,city,level
GROUPING SETS (sex,city,level,(sex,city),
(sex,level),(city,level),(sex,city,level));
优化后:
select sex
city,
level,
count(distinct user_id)
FROM user_info
GROUP BY sex,city,level
with cube;
rollup:以最左侧的维度为主,进行层级聚合,是cube的子集
例如:统计每个月的支付金额,以及每年的总支付金额
SELECT a.dt,
sum(a.year_amount),
sum(a.month_amount)
FROM
(SELECT substr(dt,1,4) as dt,
sum(pay_amount) year_amount,
0 as month_amount
FROM user_trade
WHERE dt>'0'
GROUP BY substr(dt,1,4)
UNION ALL
SELECT substr(dt,1,7) as dt,
0 as year_amount,
sum(pay_amount) as month_amount
FROM user_trade
WHERE dt>'0'
GROUP BY substr(dt,1,7)
)a
GROUP BY a.dt;
优化后:
SELECT year(dt) as year,
month(dt) as month,
sum(pay_amount)
FROM user_trade
WHERE dt>'0'
GROUP BY year(dt),
month(dt)
with rollup;
Hive中互相没有依赖关系的job间是可以并行执行的,最典型的就是多个子查询union all。在集群资源相对充足的情况下,可以开启并行执行。
参数设置:
set hive.exec.parallel=true;
例如:统计每个用户的支付和退款金额汇总
SELECT a.user_name,
sum(a.pay_amount),
sum(a.refund_amount)
FROM
( SELECT user_name,
sum(pay_amount) as pay_amount,
0 as refund_amount
FROM user_trade
WHERE dt>'0'
GROUP BY user_name
UNION ALL
SELECT user_name,
0 as pay_amount,
sum(refund_amount) as
refund_amount
FROM user_refund
WHERE dt>'0'
GROUP BY user_name
)a
GROUP BY a.user_name;
-- 时间对比:
-- 未开并发执行 103 s
-- 开启并发执行 64 s
Join操作的原则
原则1:应该将条目少的表、子查询放在join操作符的左边。
新版hive已经对小表join大表和大表join小表进行了优化。小表放左边和右边已经没有明显区别。不过在做join的过程中通过小表在前可以适当减少数据量,提高效率。
原则2:使用相同的连接键
当对3个或者更多个表进行join连接时,如果每个on子句都使用相同的连接键的话,那么只会产生一个MapReduce job。
原则3:尽早的过滤数据
减少每个阶段的数据量,对于分区表要加分区,同时只选择需要使用到的字段。
-- 开启map端预聚合功能
set hive.map.aggr=true;
-- 开启group by倾斜时自动进行负载均衡功能
set hive.groupby.skewindata = ture;
具体步骤如下:
严格模式:强制不允许用户执行3种有风险的Hive SQL语句,一旦执行会直接报错。
set hive.mapred.mode=strict
3种有风险的Hive SQL语句:
好了,今天就为大家分享到这里了。咱们下期见!
如果本文对你有帮助的话,欢迎点赞&收藏&分享,这对我继续分享&创作优质文章非常重要。感谢