作为一个工作在Hadoop领域的数据科学家,我经常使用Apache Hive 来探索数据,进行ad-hoc查询,或构建数据流。

  直到现在,优化Hive查询的方向大部分聚焦在数据层次技术,例如分区,或分桶,或定制个性化文件格式。

  在最近几年,围绕着Hive社区的Stinger Initiative 的创新驱动,Hive的查询时间有了明显的提升,这使得Hive能够在速度和尺度上较好的支持批处理和交互式查询。

  然而,许多数据科学教依然不熟悉这些能够使Hive查询最快运行的基础技术手段和最佳实践。在本篇博文中,我展示了几种平时用到的简单技术手段来提高Hive查询的性能。

使用Tez

  Hive可以使用Apache Tez 作为执行引擎,替换传统的Map-Reduce引擎。我不会在这里细说Tez更多的详细细节,相反,我只是做一个简单的推荐:如果在你的环境中没有开启,那么可以通过如下设置在你的Hive查询中开启Tez

set hive.execution.engine=tez;

通过以上的设置,执行的Hive查询可以充分使用Tez的优势。

使用ORCFile

  Hive支持ROCFILE文件格式,一种新的表数据存储格式,其通过一些技术支持可观的查询速度提升,这些技术包括预测性下钻、压缩等技术手段。

  对每一张表使用ORCFILE文件格式,对于Hive查询的性能提升应该是非常轻松和巨大的好处。

  作为示例,假设两张大表A和B(存储为文本格式,这里不具体列出表中列的信息)。一个简单的查询如下

SELECT A.customerID, A.name, A.age, A.address join

B.role, B.department, B.salary

ON A.customerID=B.customerID;

This query may take a long time to execute since tables A and B are both stored as TEXT. Converting these tables to ORCFile format will usually reduce query time significantly:

CREATE TABLE A_ORC (

customerID int, name string, age int, address string

) STORED AS ORC tblproperties (“orc.compress" = “SNAPPY”);
INSERT INTO TABLE A_ORC SELECT * FROM A;
CREATE TABLE B_ORC (

customerID int, role string, salary float, department string

) STORED AS ORC tblproperties (“orc.compress" = “SNAPPY”);
INSERT INTO TABLE B_ORC SELECT * FROM B;
SELECT A_ORC.customerID, A_ORC.name,

A_ORC.age, A_ORC.address join

B_ORC.role, B_ORC.department, B_ORC.salary

ON A_ORC.customerID=B_ORC.customerID;

  ORC支持压缩存储,也可以不压缩存储。如压缩存储,支持ZLIB和SNAPPY压缩格式。

  将表格的数据存储格式更改为ORC,通常是本团队的职责。根据他们的工作排班,这通常需要花费一些时间。受众于ORCFILE的绝大好处,我通常建议将文本存储格式转化为ORC存储格式。这样的话,查询通常会有明显的速度提升,而不需要依赖其他团队。

使用向量化

  支持向量化查询,能够提升一些操作的性能,例如扫描、聚合、过滤和join操作。通过一次1000条的批处理,替代每次一条的处理方式。

  Hive0.13引入了向量化支持,这个特性显著地提升了查询的执行时间,并且该特性能够方便的通过以下两个参数设置

set hive.vectorized.execution.enabled=true;
set hive.vectorized.execution.reduce.enable=true;

基于代价的优化:CBO

  Hive在最终执行之前,会进行查询语句的逻辑优化和物理执行计划的优化。这些优化并不是基于CBO的,直到现在。

  最近,CBO被加入到Hive中,基于代价优化能够进一步优化性能,导致了一些潜在的不同选择:如何组织join,什么类型的join可以优化,不支持并行化处理等等。

  使用CDO优化,在查询之前设置一下参数

set hive.cbo.enable=true;
set hive.compute.query.using.stats=true;
set hive.stats.fetch.column.stats=true;
set hive.stats.fetch.partition.stats=true;

这些设置之后,通过运行Hive的analyze能够收集CDO优化目标的多种统计参数。

使用好SQL

  SQL是一种非常强大的声明式语言。像其他声明式语言,存在不止一种写SQL语句的方式。虽然语句的功能相同,但可能存在多种不同的表达式。

  作为一个示例,考虑如下表

CREATE TABLE clicks (
timestamp date, sessionID string, url string, source_ip string
) STORED as ORC tblproperties (“orc.compress” = “SNAPPY”);
Each record represents a click event, and we would like to find the latest URL for each sessionID;

通过如下方式同样可以实现该功能

SELECT clicks.* FROM clicks inner join
(select sessionID, max(timestamp) as max_ts from clicks
group by sessionID) latest
ON clicks.sessionID = latest.sessionID and
clicks.timestamp = latest.max_ts;

以上查询,我们构建了一个子查询来收集每个session中最近事件的时间戳,并使用一个inner join来关联过滤外部的结果。

  虽然这种一种从功能性上考虑的实现方式,实际上还存在更佳的实现方式

SELECT * FROM
(SELECT *, RANK() over (partition by sessionID,
order by timestamp desc) as rank
FROM clicks) ranked_clicks
WHERE ranked_clicks.rank=1;

这里使用了OLAP函数(over,rank)来达到同样的功能,而不适用join。

  显然移除了非必要的join操作几乎导致更好的表现性能,并当数据量较大时,这种性能差异更大。通常,许多查询语句并不是最优的,因此需要仔细查看每一个SQL语句,是不是存在优化空间。

总结

  Apache Hive是数据分析师、数据科学家手中强大的工具,并支持批处理和实时交互查询。

  在本博文中,我讨论了一些有用的技巧来提升Hive查询的性能,这些技巧都是我日常工作中常用到的。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Netty权威指南读书笔记 上一篇
Hive相关的两个疑问 下一篇