Hive零基础从入门到实战 进阶篇(二十) Hive常用调优

目录

 

前言

1. HiveQL实现流程优化

1.1 使用with as语法

1.2 分区剪裁与列剪裁

1.3 where条件中尽量不要加函数

1.4 函数不要嵌套太多层

1.5 在每一个子查询中使用group by去重

1.6 join优化

1.6.1 小表放左边

1.6.2 key保持一致

1.6.3 Map join

1.6.4 多用left join

1.7 数据倾斜

2. 参数设置优化


 

前言

由于Hive是将符合SQL语法的字符串解析生成可以在Hadoop上执行的MapReduce的工具,所以运行速度十分感人,有时候甚至慢到你怀疑人生。但其实只要你掌握一些常见的简单调优手段,就可以大幅提高Hive跑数的速度。本文会介绍一些常用的Hive调优小技巧,给整个进阶篇画上一个句号。

 

1. HiveQL实现流程优化

使用Hive尽量按照分布式计算的一些特点来设计sql,和传统关系型数据库有区别,所以必须去掉原有关系型数据库下开发的一些固有思维。

 

1.1 使用with as语法

在入门篇(十八)中已经介绍过with as的用法和优点,主要有以下几点:

  • 代替多层子查询,提高HQL语句的可读性;
  • 会将表预先加载入内存,代码运行效率高;
  • 可以将多次访问的表放在一个WITH AS语句中,这样访问该表一次即可,提高了代码运行效率。

 

1.2 分区剪裁与列剪裁

多表join时尽早过滤数据,减少每个阶段的数据量,对于分区表必须通过子查询限制分区,同时只select需要使用的字段。

举例:

优化前写法:

SELECT a.platform
      ,a.app_version
      ,count(a.user_id) AS num
FROM app.t_od_use_cnt a
INNER JOIN app.t_od_new_user b
      ON a.user_id = b.user_id
WHERE a.date_8 = 20190101
      AND a.is_active = 1
      AND b.date_8 = 20190101
GROUP BY platform
      ,app_version;

这种写法从语法上来说其实是没有错误的,但极其浪费资源,运行速度慢,因为运行时会先扫描两张表所有的数据,然后进行关联。最后才会通过where条件和select语句筛选出来所需数据。

 

优化后写法:

先进行分区剪裁和列剪裁,再进行关联操作:

SELECT a.platform
      ,a.app_version
      ,count(a.user_id) AS num
FROM (
   --20190101的平台、版本、活跃用户id
      SELECT platform
            ,app_version
            ,user_id
      FROM app.t_od_use_cnt
      WHERE date_8 = 20190101
            AND is_active = 1
      )a
INNER JOIN (
   --20190101的新增用户id
      SELECT user_id
      FROM app.t_od_new_user
      WHERE date_8 = 20190101
      )b
      ON a.user_id = b.user_id
GROUP BY platform
      ,app_version;

 

最终优化写法:

改进成with as写法:

WITH a
AS (
   --20190101的平台、版本、活跃用户id
      SELECT platform
            ,app_version
            ,user_id
      FROM app.t_od_use_cnt
      WHERE date_8 = 20190101
            AND is_active = 1
      )
      ,b
AS (
   --20190101的新增用户id
      SELECT user_id
      FROM app.t_od_new_user
      WHERE date_8 = 20190101
      )
   --20190101的平台、版本、新增活跃用户数
SELECT a.platform
      ,a.app_version
      ,count(a.user_id) AS num
FROM a
INNER JOIN b
      ON a.user_id = b.user_id
GROUP BY platform
      ,app_version;

 

1.3 where条件中尽量不要加函数

如果select和where后对同一个字段要使用同样的函数进行处理,那么先在select中进行处理,然后套一层子查询进行where条件限制。因为复杂函数十分耗费资源,能只用一遍就只用一遍。

举例:

想要取出20190101的奇数尾号用户id

优化前:

SELECT date8
      ,PARSE_URL('http://a.qq.com' || url, 'QUERY', 'uid') AS uid
FROM t1
WHERE PARSE_URL('http://a.qq.com' || url, 'QUERY', 'uid') % 2 = 1
      AND date8 = '20190101'

PARSE_URL函数的用途是解析url,是一个很浪费资源的函数,所以尽量避免在where和select中二次使用。

优化后:

SELECT date8
      ,uid
FROM (
      SELECT date8
            ,PARSE_URL('http://a.qq.com' || url, 'QUERY', 'uid') AS uid
      FROM t1
      WHERE date8 = '20190101'
      ) a
WHERE uid % 2 = 1

这种优化只有在数据量较大时才会体现出效果,因为优化前的写法只有一个job,而优化后出现了子查询变成了两个job,如果数据量较小,启动job的时间反而会长于函数运算的时间。

 

1.4 函数不要嵌套太多层

函数嵌套层数过多会大幅拖慢运行速度,多熟悉Hive各种函数的用法(多看几遍进阶篇,哈哈),实现方式往往不只一种,选最简单的方法实现需求。

 

1.5 在每一个子查询中使用group by去重

使用子查询或者with as语句创建临时视图时,最好对每一块数据集进行去重处理,去重时使用group by,不要使用distinct。去重是为了提前缩小数据量,使用group by不用distinct是因为数据量大时group by去重更快。

 

1.6 join优化

1.6.1 小表放左边

大表和小表关联时,将数据量小的表放在join左边。原因是在 join 操作的 Reduce 阶段,位于 join左边的表会被加载进内存,将条目少的表放在左边,可以有效减少发生内存溢出错误的几率。

关联时数据量小的表放在join左边
SELECT a.col
      ,b.col
FROM 小表 a
INNER JOIN 大表 b
      ON a.key = b.key1

1.6.2 key保持一致

join查找操作中如果存在多个join,且所有参与join的表中其参与join的key都相同,则会将所有的join合并到一个mapreduce程序中。 最优的关联方式是所有表都直接要和主表关联,且key保持一致。

举例:

关联时都关联同一个表,并且on后面的key一致,这样会在一个mapreduce程序中执行join
SELECT a.col
      ,b.col
      ,c.col
FROM a
INNER JOIN b
      ON a.key = b.key1
INNER JOIN c
      ON a.key = c.key1;

关联时没有都关联同一个表,并且on后面的key不一致,会在两个mapreduce程序中执行join,拖慢运行速度
SELECT a.col
      ,b.col
      ,c.col
FROM a
INNER JOIN b
      ON a.key = b.key1
INNER JOIN c
      ON c.key = b.key2;

1.6.3 Map join

MapJoin是在Map阶段进行表之间的连接。而不需要进入到Reduce阶段才进行连接。这样就节省了在Shuffle阶段时要进行的大量数据传输。从而起到了优化作业的作用。适用于一个很大的表关联一个很小的表,这个小表可以存放在内存中而不影响性能。这样我们就把小表文件复制到每一个Map任务的本地,再让Map把文件读到内存中待用。

在Hive 0.7之前,需要使用 /*+ mapjoin(table) */才会执行MapJoin  。但是Hive v0.7之后的版本已经不需要手动设置就会进行优化。默认小表小于25M就会开启MapJoin优化。

设置参数为:set hive.auto.convert.join=true; 默认为true。

1.6.4 多用left join

抽取数据的时候使用核心表去left join其他表,保证核心表中所有数据都在,即使匹配不到也会存在Null而不是数据的丢失。

使用核心表去left join其他表
SELECT a.col
      ,b.col
FROM 核心表 a
LEFT JOIN 其他表 b
      ON a.key = b.key1

1.7 数据倾斜

数据倾斜是指:并行处理的数据集中,某一部分数据显著多于其它部分,从而使得该部分的处理速度成为整个数据集处理的瓶颈。 表现为整体任务基本完成,但仍有少量子任务的reduce还在运行。进度卡在99%或100%一直无法完成。

造成数据倾斜的原因一般有以下三种:

1. 大表与大表关联,如果用来连接的字段(on后面的)null值过多,这些null值都会由一个reduce处理。

解决方法:

1)如果null值所对应的的数据都不需要的话,可以直接在on后面将表中的null值过滤掉。

SELECT *
FROM a
INNER JOIN b
      ON a.user_id IS NOT NULL
            AND a.user_id = b.user_id

2)如果null值所对应的的数据仍然需要的话, 可以将null值替换为随机数进行关联。

SELECT *
FROM a
LEFT JOIN b
      ON CASE 
            WHEN a.user_id IS NULL
                  THEN CONCAT ('hive',RAND())
            ELSE a.user_id
            END = b.user_id;

2. group by时分组的维度过少,每个维度的值过多,导致处理某值的reduce耗时很久,比如根据性别进行group by,只有男女两类,每个类别里的数据量就可能过大。

解决方法:

设置下如下参数:

参数设置 默认值及含义
set hive.map.aggr=true; 是否在 Map 端进行聚合,默认为true
set hive.groupby.mapaggr.checkinterval=1000000; 在 Map 端进行聚合操作的条目数目,默认为10万条
set hive.groupby.skewindata=true; 数据倾斜聚合优化,用于负载均衡。默认 false

3.count(distinct col)时col中某个特殊值相同的过多,处理特殊值耗时。

解决方法:

先用group by去重,然后嵌套子查询用count(1)

优化前写法:

SELECT platform
      ,count(DISTINCT version) num
FROM a
GROUP BY platform;

优化后写法:

SELECT platform
      ,count(1) num
FROM (
      SELECT platform
            ,version
      FROM a
      GROUP BY platform
            ,version
      ) a
GROUP BY platform;

2. 参数设置优化

常用优化参数如下表,除了第三条和最后一条,其余参数建议直接写进家目录的.hiverc文件中,每次启动hive自动加载这些参数设置:

参数设置 默认值及含义
set hive.map.aggr=true; 是否在 Map 端进行聚合,默认为true
set hive.groupby.mapaggr.checkinterval=1000000; 在 Map 端进行聚合操作的条目数目,默认为10万条
set hive.groupby.skewindata=true; 数据倾斜聚合优化,用于负载均衡。默认 false。开启后一个select语句中只能使用一个count(distinct)。多于一个会报错。
set hive.optimize.groupby=true; 优化group by,默认为true
set hive.optimize.union.remove=true; 在大量union情况下进行优化,默认false,需要带有partition的表才能用
set hive.exec.parallel=true; 并行执行嵌套select,默认 false,强烈建议开启
set hive.exec.parallel.thread.number=16; 执行嵌套sql最大并行数,默认为8,可以调成16
set hive.exec.mode.local.auto=true;

开启本地模式,在单台机器上处理所有的任务。对于小数据集,执行时间会明显被缩短。默认为false

当一个job满足如下条件才能真正使用本地模式:
1.job的输入数据大小必须小于参数:hive.exec.mode.local.auto.inputbytes.max(默认128MB)
2.job的map数必须小于参数:hive.exec.mode.local.auto.tasks.max(默认4)
3.job的reduce数必须为0或者1

 

 


能看到这里的同学,就右上角点个赞吧,3Q~

 

你可能感兴趣的:(Hive零基础从入门到实战 进阶篇(二十) Hive常用调优)