- 使用 hint 来改写执行计划
select a.*, b.*
from fctOrders a
inner join employees b on a.employee_id = b.employee_id
显然 fctOrder 表的记录要比 employees 多上好几个数量级。将 fctOrders 放在第一位导致第一遍 map 跑批的数据量增大。因此当尽量在 join 的左边用小表。
有了 hint, 就不必在意 join 两表的顺序了:
代码语言:javascript复制select /* STREAMTABLE(a) */ a.*,b.*
from fctOrders a
inner join employees b on a.employee_id = b.employee_id
- 使用配置来改写执行计划
同样是将 join 的两表进行位置互换,这一次是使用配置。所有的配置需要固化下来,都可以放在 $HOME/.hiverc 文件中
代码语言:javascript复制set hive.auto.convert.join =true ;
select a.*, b.*
from fctOrders a
inner join employees b on a.employee_id = b.employee_id
开启之后,小表优先进入内存进行 MapJoin 操作。
- 使用 partition
针对大数据量的事实表做分区,比如按月做分区,那么查询每个月的基本数据量时,只需扫描单个分区即可,而不必要扫描整张大表。假设极限情况下,所有其他月的数据并不够多,而只是其中一个月的数据量很大,那么只有对这个月的数据进行有效分区之后,才能真正达到高效。
分区本质上还是分而治之,但如果分区数据并不是分布在每台集群中的服务器上,仅仅是存储在其中一台服务器上,分区也没有太大意义,在这种情况下就变成了单实例的数据库。除非分区在分布式集群中,也是均摊到各个集群节点服务器上。
- 使用 sequencefile 存储格式
当hdfs上的文件小于一个hdfs block的时候,被称作小文件。处理小文件并不是 hadoop 的专长,同样也不是 hive 的专长。碰到这类问题,就需要将小文件统统归档到一个大文件里面去,比如 sequencefile.
假设 /user/hive/warehouse/logs 下面很多 10MB 的 log 文件,我们可以用 text file 来将整个目录下的文件都从逻辑上归档到一张表 temp_table 中去,再创建一张 sequenfile 表,将 temp_table中的数据装载到 sequencefile 中。
- 使用 ORCfile 存储:
官方说明,predicate-push-down, compression 等技术使得 ORCfile 在 join 两张大表的时候,更能体现性能的优势。来自 hortonworks 的论证, 对每张表都采用 ORCfile 格式存储,已经是个不争的技巧。
- 使用 Apache Tez 执行引擎
Tez 是基于 YARN 的一个计算引擎。配置 Tez 对于 Hive 有益的地方在于有效利用 YARN 带来的比 MapReduce 1 优异的性能。其中之一就是有效利用每台节点服务器的内存,防止浪费,也有效防止因数据得不到充足的内存而故障造成的任务延迟。在最终的结果生成时,有效利用并行输出也是提高整体 HQL 的一环。
代码语言:javascript复制SET hive.tez.auto.reducer.parallelism=true;
- 使用 vectorization 技术
set hive.vectorized.execution.enabled=true ;
set hive.vectorized.execution.reduce.enabled=true;
在计算类似 scan, filter, aggregation 的时候, vectorization 技术以设置批处理的增量大小为 1024 行单次来达到比单条记录单次获得更高的效率。
7.1 cost based query optimization
成本优化器:传统的数据库,成本优化器做出最优化的执行计划是依据统计信息来计算的。Hive 的成本优化器也一样。
代码语言:javascript复制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 ;
以上开启了 cost based optimization.
下面的命令重新计算了统计信息:
代码语言:javascript复制analyze table tweets compute statistics;
analyze table tweents compute statistics for columns sender, topic;
analyze table tweens compute statistics for columns ;
- 语句级别的调优
比如: row_number 可以解决 Hive 级别的 Join 导致的网络传输速度慢的问题,但在 sql server 中就不能这么用了。
代码语言:javascript复制select tmp.*
from (select a, row_number()over(partition by category order by orderDate desc) as rnk
from fctOrder
) tmp
where rnk = 1
代码语言:javascript复制select a.*
from fctOrder a
inner join (select category,max(orderDate) as maxDate from fctOrder
group by category ) tmp on a.category = tmp.category
and a.orderDate = tmp.maxDate
这类应用在 sql server 和 Hive 中各自的效率是不一样的。
8.1 避免使用 SELECT COUNT(Distinct field) FROM tblTable
使用
代码语言:javascript复制SELECT COUNT(1) AS TotalCount
FROM (
SELECT Distinct field
FROM tblTable
)Tmp
这里最重要的思想便是将所有的计算拆成利用多个 reducer 进行计算的模式,而不是将全部的计算都压到一个 reducer.
8.2 慎重考虑用于分组的笛卡尔运算:
good:SELECT GROUP BY userId, gender bad: SELECT GROUP BY gender,userId
userId 在前面,可利用多个 reducer 进行分组运算,而 gender 则之多会应用 3 个 reduer. 一是男,二是女,三未知。
8.3 Each-Top-N 的求解
每组前 N 个的应用场景,在数据分析领域常用。在 SQL 中一般用窗口函数 Rank()Over(Partition By)来计算。
而 Hive 中为了更好的发挥分布式运算,需要利用多个 reducer 来处理。
代码语言:javascript复制SELECT *,Rank()Over(Partition BY)
FROM tblTable
Distribute By FieldA
Sort By FieldA, MetricA DESC
- 使用 predicate pushdown (PPD) 在存储级别执行过滤
SET hive.optimize.ppd=true ;
SET hive.optimize.ppd.storage=true ;