Hive性能调优实践 - Vidhya


Apache Hive 是一个建立在 Hadoop 之上的数据仓库系统,它使用户能够灵活地以类似 SQL 的查询的形式编写复杂的 MapReduce 程序。性能调优是运行 Hive 查询的重要部分,因为它可以帮助程序员减少时间并提高代码效率。应用正确的方法并使用最佳优化实践,我们可以在 hive 查询的时间和存储方面取得重大的里程碑,并提高集群的性能,而性能调优在这里发挥了重要作用。 
在这篇文章中,我想分享一些性能调优配置和查询的实践,我发现这些实践在使用 Hive 时很有效。
首先,让我们了解调整 Hive 查询的步骤:

  1. 识别:在这一步中,我们列出了所有消耗更多时间的 Hive 代码。
  2. 调整:我们在不干扰代码功能的情况下找出最佳解决方案,并确保改进代码。
  3. 测试和部署:我们测试更改的代码,然后将其部署到生产中。我们应该监控几天更改代码的运行时间。
  4. 测量: 在这一步中,我们随时测量更改的代码是否在改进。此外,将更改代码的新运行时间与过去的生产时间进行比较。
  5. 继续改进: 我们一直在寻找可能的解决方案以进一步改进。

 
性能调优技术的类型
有几种 Hive 优化技术可以提高其性能,我们可以在运行 Hive 查询时实施这些技术,从而也关注性能调优:
1 避免锁定表
确保在任何 Hive 查询中都使用表是非常重要的,因为源没有被另一个进程使用。这可能导致表被锁定,我们的查询可能会被卡住一段未知的时间。
我们可以使用下面的参数来确保表没有被锁定:
set hive.support.concurrency=false;
set hive.txn.manager=org.apache.hadoop.hive.ql.lockmgr.DummyTxnManager;
set hive.bin.strict.locking.mode=false;

 
2 使用Hive的执行引擎TEZ
在执行Hive查询时,TEZ执行引擎是首选,因为它消除了不必要的磁盘访问。它从磁盘中获取一次数据,进行计算,并产生输出,从而使我们免于多次磁盘遍历。我们可以认为TEZ是map-reduce框架的一个更灵活、更强大的继承者。

我们可以设置下面的参数来使用TEZ引擎。

[i]set hive.execution.engine=tez;[/i]

  
3 使用Hive基于成本的优化器(CBO)
Apache Hive提供了一个基于成本的优化器来提高性能。它通过检查查询成本来生成有效的执行计划,比如如何排序连接、执行哪种类型的连接、并行程度等等。这些决定由ANALYZE语句或元存储本身收集,最终削减查询执行时间并减少资源利用率。

我们可以来设置参数。

set hive.cbo.enable=true;

 
4 在Mapper和Reducer层面的并行执行
我们可以通过使用矢量查询执行来提高hive查询的聚合、过滤和连接的性能,这意味着我们可以一次性分批扫描1024条记录,而不是每次都扫描一条记录。

我们应该探索以下的参数,这将有助于带来更多的并行性,并大大改善查询执行时间。

set hive.vectorized.execution.enabled=true
set hive.exec.parallel=true;

类如:

Select a.*, b.* from
(select * from table1 ) a
Join
(select * from table2 ) b
On a.id=b.id
;

我们可以看到这两个子查询是独立的,所以这可能会增加效率。

需要注意的一个重要问题是,并行执行会增加集群的利用率。如果一个集群的利用率已经很高了,并行执行就不会有什么帮助。
 
5 使用STREAMTABLE选项
当我们要连接多个表时,我们可以使用STREAMTABLE选项。默认情况下,最右边的表会被流化。

比如说。如果我们连接两个表'huge_table'和'small_table',默认情况下,'small_table'被流化,因为它是最右边的表。在这种情况下,'huge_table',作为较大的表,将试图被缓冲到内存中,并可能导致java堆空间问题或作业运行时间更长。在这种情况下,我们可以做的是添加/*+ STREAMTABLE('huge_table') */,这将使'huge_table'被流化而不是进入内存。

因此,通过这种方式,我们可以不用记住连接表的顺序。
 
6 使用Map Side JOIN选项
如果连接中的一个表是一个小表,并且可以被加载到内存中,我们可以强制使用MAPSIDE连接,如下所示。

Select /*+ MAPJOIN(small_table) */ large_table.col1,large_table.col2 from large_table join small_table on large_table.col1 = small_table.col1;

map端连接可以在映射器中执行,而无需使用Map/Reduce步骤。

另外,我们可以通过设置auto.convert.join为True,让执行引擎来处理这个问题。

set auto.convert.join = True :

 
7.避免在JOIN和WHERE子句中使用计算字段
我们应该避免在JOIN和WHERE子句中使用任何计算字段,因为它们需要花费很长的时间来运行Hive查询。我们可以使用CTE(Create table expression)来处理这些功能,并且可以优化我们的查询。

比如说, 原始查询:

select a.coll, b.col2 from table1 as a join table2 as b on (a.coll +50 = b.col2);

使用CTE 优化后:

(select a.col1 + 50 as C1 FROM table1 )
select CTE.C1, b.col2 from CTE join table2 b on (CTE.C1 = b.col2);

 
8 使用SORT BY而不是ORDER BY
Hive支持ORDER BY和SORT BY两种原因。ORDER BY在单一的还原器上工作,它会导致性能瓶颈。但是,SORT BY只在每个减速器内对数据进行排序,并执行本地排序,每个减速器的输出都将被排序,确保更好的性能。

9 选择需要的列
当我们使用Hive时,如果我们只需要一个表中的几列,请避免使用SELECT * FROM,因为这会增加不必要的执行时间。
 
10 适当的输入格式选择
在数据的基础上使用合适的文件格式可以大大增加我们的查询性能。Hive带有柱状输入格式,如RCFile、ORC等。与Text、Sequence和RC文件格式相比,ORC显示出更好的性能,因为Hive有一个矢量的ORC阅读器,通过允许单独访问每一列,可以减少分析查询中的读取操作。
 
11 尽可能早地限制(过滤)数据 
这是任何调整的基本原则,在这个过程中,我们提前过滤或放弃记录,这样就可以避免处理长期运行的查询和处理不必要的结果。
例如:
a join b where a.col !=null
写成:

 (select * from a where a.col!=null) join b

  
12 使用多查询插入
在这里,我们将有一个共同的数据集(Superset),然后我们将根据WHERE子句中指定的特定条件,使用它来插入多个表。当多个表有共同的超级数据集时,这有助于并行加载这些表。
 
13 将代码模块化为逻辑片段
这有助于对代码的维护。也有助于在失败的情况下重新启动工作。这可以通过运行相互独立的模块来实现并行化,从而节省时间。