您的位置:首页 > 博客中心 > 数据库 >

数据仓库中的 SQL 性能优化(Hive篇)

时间:2022-03-13 23:12

一个Hive查询生成多个map reduce job,一个map reduce job又有map,reduce,spill,shuffle,sort等多个阶段,所以针对hive查询的优化可以大致分为针对MR中单个步骤的优化(其中又会有细分),针对MR全局的优化,和针对整个查询(多MR job)的优化,下文会分别阐述。

gxlsystem.com,布布扣

另外对于特殊值的处理往往跟业务有关系,所以也可以从业务角度重写sql解决。比如前面这种倾斜join,可以把特殊值隔离开来(从业务角度说,users表应该不存在user_id = 0的情况,但是这里还是假设有这个值,使得这个写法更加具有通用性):

SELECT a.*
FROM
  (SELECT a.*
   FROM
     (SELECT *
      FROM logs
      WHERE user_id = 0) a
   JOIN
     (SELECT *
      FROM users
      WHERE user_id = 0) b ON a.user_id = b.user_id
   UNION ALL SELECT a.*
   FROM logs a
   JOIN users b ON a.user_id <> 0
   AND a.user_id = b.user_id)t;
数据倾斜不仅仅是hive的问题,其实是share nothing架构下必然会碰到的数据分布问题,对此学界也有专门的研究,比如 。

6、SQL整体优化

前面对于单个job如何做优化已经做过详细讨论,但是hive查询会生成多个job,针对多个job,有什么地方需要优化?

6.1 Job间并行

首先,在hive生成的多个job中,在有些情况下job之间是可以并行的,典型的就是子查询。当需要执行多个子查询union all或者join操作的时候,job间并行就可以使用了。比如下面的代码就是一个可以并行的场景示意:
select * from 
(
   select count(*) from logs 
   where log_date = 20130801 and item_id = 1
   union all 
   select count(*) from logs 
   where log_date = 20130802 and item_id = 2
   union all 
   select count(*) from logs 
   where log_date = 20130803 and item_id = 3
)t
设置job间并行的参数是hive.exec.parallel,将其设为true即可。默认的并行度为8,也就是最多允许sql中8个job并行。如果想要更高的并行度,可以通过hive.exec.parallel. thread.number参数进行设置,但要避免设置过大而占用过多资源。

6.2 减少Job数

另外在实际开发过程中也发现,一些实现思路会导致生成多余的job而显得不够高效。比如这个需求:查询某网站日志中访问过页面a和页面b的用户数量。低效的思路是面向明细的,先取出看过页面a的用户,再取出看过页面b的用户,然后取交集,代码如下:
SELECT count(*)
FROM
  (SELECT DISTINCT user_id
   FROM logs
   WHERE page_name = ‘a‘) a
JOIN
  (SELECT DISTINCT user_id
   FROM logs
   WHERE blog_owner = ‘b‘) b ON a.user_id = b.user_id;
这样一来,就要产生2个求子查询的job,一个用于关联的job,还有一个计数的job,一共有4个job。
但是我们直接用面向统计的方法去计算的话(也就是用group by替代join),则会更加符合M/R的模式,而且生成了一个完全不带子查询的sql,只需要用一个job就能跑完:
select count(*) 
from logs group by user_id
having (count(case when page_name = ‘a‘ then 1 end) > 0
    and count(case when page_name = ‘b‘ then 1 end) > 0)

第一种查询方法符合思考问题的直觉,是工程师和分析师在实际查数据中最先想到的写法,但是如果在目前hive的query planner不是那么智能的情况下,想要更加快速的跑出结果,懂一点工具的内部机理也是必须的。

当然了,也有同学有其它的思路,只是没有上面那么高效:

select count(*) from
(
    select user_id,
    count(case when blog_owner = ‘a‘ then 1 end) as visit_z,
    count(case when blog_owner = ‘b‘ then 1 end) as visit_l
    from cnblogs_visit_20130801 group by user_id
) t
where visit_z > 0 and visit_l > 0;
这种实现方式转换成job就只会有2个:内层的子查询和外层的统计,所以对 SQL 和原理都比较熟悉才能在 HIVE 中游刃有余~

7、Refer:

[1] 数据仓库中的SQL性能优化(Hive篇)

热门排行

今日推荐

热门手游