Skip to content

Hive其他常用优化

除了之前介绍的比较大的优化方面之外,还有以下一些琐碎的优化点,比较简单👻主要是涉及特定的开关的开闭参数调整。

1. CBO优化

CBO是指Cost Based Optimizer,即基于计算成本的优化。
在Hive中,计算成本模型考虑到了:数据的行数、CPU、内存、本地IO、HDFS IO、网络IO等方面。Hive会计算同一SQL语句的不同执行计划的计算成本,并选出成本最低的执行计划。目前CBO在hive的MR引擎下主要用于join的优化,例如多表join的join顺序。相关参数为:

sql
-- 是否启用cbo优化 
set hive.cbo.enable=true;

1.2 优化实操

  1. SQL背景
sql
select
    *
from order_detail od
join product_info product on od.product_id=product.id
join province_info province on od.province_id=province.id;
  1. 关闭CBO优化
sql
-- 关闭cbo优化 
set hive.cbo.enable=false;
-- 为了测试效果更加直观,关闭map join自动转换
set hive.auto.convert.join=false;

根据执行计划,可以看出,三张表的join顺序如下:
Alt text
3. 开启CBO优化

sql
-- 开启cbo优化 
set hive.cbo.enable=true;
-- 为了测试效果更加直观,关闭map join自动转换
set hive.auto.convert.join=false;

根据执行计划,可以看出,三张表的join顺序如下:
Alt text

1.3 总结

根据上述案例可以看出,CBO优化对于执行计划中join顺序是有影响的,其之所以会将province_info的join顺序提前,是因为province info的数据量较小,将其提前,会有更大的概率使得中间结果的数据量变小,从而使整个计算任务的数据量减小,也就是使计算成本变小。

2. 谓词下推

谓词下推(predicate pushdown)是指,尽量将过滤操作前移,以减少后续计算步骤的数据量。相关参数为:

sql
-- 是否启动谓词下推(predicate pushdown)优化
set hive.optimize.ppd = true;

需要注意的是:
CBO优化也会完成一部分的谓词下推优化工作,因为在执行计划中,谓词越靠前,整个计划的计算成本就会越低, CBO优化在考量最少成本计算就会执行谓词下推,总之CBO优化里面会做很多工作。

2.1 优化实操

  1. SQL背景
sql
select
    *
from order_detail
join province_info
where order_detail.province_id='2';
  1. 关闭谓词下推优化
sql
-- 是否启动谓词下推(predicate pushdown)优化
set hive.optimize.ppd = false;
-- 为了测试效果更加直观,关闭cbo优化
set hive.cbo.enable=false;
-- 为了测试效果更加直观,关闭map join自动转换
set hive.auto.convert.join=false;

通过执行计划可以看到,过滤操作位于执行计划中的join操作之后。
Alt text
3. 开启谓词下推优化

sql
-- 是否启动谓词下推(predicate pushdown)优化
set hive.optimize.ppd = true;
-- 为了测试效果更加直观,关闭cbo优化
set hive.cbo.enable=false;
-- 为了测试效果更加直观,关闭map join自动转换
set hive.auto.convert.join=false;
  1. 通过执行计划可以看出,过滤操作位于执行计划中的join操作之前 Alt text

3. 矢量化查询

Hive的矢量化查询优化,依赖于CPU的矢量化计算,CPU的矢量化计算的基本原理如下图:
Alt text Hive的矢量化查询,可以极大的提高一些典型查询场景(例如scans, filters, aggregates, and joins)下的CPU使用效率。相关参数如下:

sql
set hive.vectorized.execution.enabled=true;

若执行计划中,出现"Execution mode: vectorized"字样,即表明使用了矢量化计算,矢量计算的限制条件-官网参考连接

4. Fetch抓取

Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算。例如:select * from emp;在这种情况下,Hive可以简单地读取emp对应的存储目录下的文件,然后输出查询结果到控制台。相关参数如下:

sql
--设置在特定场景转换为fetch任务
set hive.fetch.task.conversion=[none | minimal | more];

其中可选的选项说明:

  • 设置为none表示不转换
  • 设置为minimal表示支持select *,分区字段过滤,Limit等
  • 设置为more表示支持select 任意字段,包括函数,过滤和limit等

5. 本地模式

5.1 优化说明

大多数的Hadoop Job是需要Hadoop提供的完整的可扩展性来处理大数据集的。不过,有时Hive的输入数据量是非常小的。在这种情况下,为查询触发执行任务消耗的时间可能会比实际job的执行时间要多的多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。相关参数如下:

sql
--开启自动转换为本地模式
set hive.exec.mode.local.auto=true;  
--设置local MapReduce的最大输入数据量,当输入数据量小于这个值时采用local  MapReduce的方式,
-- 默认为134217728,即128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;
--设置local MapReduce的最大输入文件个数,
-- 当输入文件个数小于这个值时采用local MapReduce的方式,默认为4
set hive.exec.mode.local.auto.input.files.max=10;

5.2 优化实操

  1. SQL背景
sql
select
    count(*)
from product_info
group by category_id;
  1. 关闭本地模式
sql
set hive.exec.mode.local.auto=false;
  1. 开启本地模式
sql
set hive.exec.mode.local.auto=true;

6. 并行执行

Hive会将一个SQL语句转化成一个或者多个Stage,每个Stage对应一个MR Job。默认情况下,Hive同时只会执行一个Stage。但是某SQL语句可能会包含多个Stage,但这多个Stage可能并非完全互相依赖,也就是说有些Stage是可以并行执行的。此处提到的并行执行就是指这些Stage的并行执行。相关参数如下:

sql
-- 启用并行执行优化
set hive.exec.parallel=true;        
-- 同一个sql允许最大并行度,默认为8
set hive.exec.parallel.thread.number=8;

6.2 优化实操

  1. SQL背景
sql
SELECT *
FROM (
SELECT user_id,
COUNT(*)
FROM 
order_detail 
GROUP BY user_id
) od
JOIN 
(
SELECT user_id, COUNT(*)
FROM payment_detail 
GROUP BY user_id
) pd ON od.user_id=pd.user_id
  1. 优化前
    可以看出,执行计划一共有3个MR,第三个MR为common join依赖前两个MR,第一个MR和第二个MR的先后并没有关系。
sql
-- 为了测试效果更加直观,关闭map join自动转换
set hive.auto.convert.join=false;

Alt text 3. 优化后

sql
-- 为了测试效果更加直观,关闭map join自动转换
set hive.auto.convert.join=false;
-- 默认关闭,启用并行执行优化
set hive.exec.parallel=true;

再次执行SQL,执行计划没有变化,查看Hadoop上执行情况可以看到前两个MR同时被提交执行:

7. 严格模式

Hive可以通过设置某些参数防止危险操作。

8.1 分区表不使用分区过滤

将hive.strict.checks.no.partition.filter设置为true时,对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。

8.2 使用order by没有limit过滤

将hive.strict.checks.orderby.no.limit设置为true时,对于使用了order by语句的查询,要求必须使用limit语句。因为order by为了执行排序过程会将所有的结果数据分发到同一个Reduce中进行处理,强制要求用户增加这个limit语句可以防止Reduce额外执行很长一段时间(开启了limit可以在数据进入到Reduce之前就减少一部分数据)。

8.3 笛卡尔积

将hive.strict.checks.cartesian.product设置为true时,会限制笛卡尔积的查询。对关系型数据库非常了解的用户可能期望在执行JOIN查询的时候不使用ON语句而是使用where语句,这样关系数据库的执行优化器就可以高效地将WHERE语句转化成那个ON语句。不幸的是,Hive并不会执行这种优化,因此,如果表足够大,那么这个查询就会出现不可控的情况。