Hive 优化总结
Hive 实际上是运行在 yarn 上的 mapreduce 任务,所以性能调优对 hql 处理数据的速度有很大的影响。
Fetch 抓取选项
Fetch 抓取是指 Hive 中某些查询可以不使用 MapReduce 计算。
例如: SELECT * FROM team;
可以简单地读取 team
对应的文件并返回结果。
配置项 hive.fetch.task.conversion
指定了哪些操作不启动 mapreduce。
1 | <property> |
本地模式
因为 Hive 背后是 Hadoop 集群,所以更适合处理大数据,但有时 Hive 中的数据比较小。这种情况下在集群中查询所消耗的时间可能会比本地处理还要长。
对于小数据集,Hive 可以使用本地模式在单台机器上处理任务,这样执行的时间可以明显被缩短。
配置项 hive.exec.mode.local.auto
的值设置为 true
,Hive 会在适当的时候自动启动这个优化。
1 | # 开启自动本地模式优化 |
查询的优化
大表,小表 join
建议将 key 相对分散且数据量小的小表放在 join 的左边,这样可以有效减少内存溢出错误发生的几率。
新版的 hive 已经对大小表之间的 join 操作做了优化。
大表 join 大表
空 key 过滤
有时 join 超时是因为某些 key 对应的数据太多,而相同 key 对应的数据都会发送到相同的 reducer 上,从而导致内存不够。此时我们应该仔细分析这些异常的 key,很多情况下,这些 key 对应的数据是异常数据,我们需要在SQL语句中进行过滤。例如 key 对应的字段为空:
1 | select p.* from (select * from player where id is not null ) p left join team on p.team_id = team.id; |
空 key 转换
有时虽然某个 key 为空对应的数据很多,但相应的数据不是异常数据,必须要包含在join的结果中,此时可以为表 a 中 key 为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的 reducer 上。
1 | select p.* from player p full join team on |
MapJoin
MapJoin 说明
如果不指定 MapJoin 或者不符合 MapJoin 的条件,那么 Hive 解析器会将 Join 操作转换成 Common Join,即:
在 Reduce 阶段完成join。这样容易发生数据倾斜,可以用 MapJoin 把小表全部加载到内存先在map端进行join,避免reducer处理。
开启 MapJoin 参数设置:
1 | # 开启自动选择 MapJoin, 默认为 true |
Group By
说明
默认情况下,Map阶段同一Key数据分发给一个reduce,当一个key数据过大时就倾斜了。并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。
开启 Map 端聚合参数设置
1 | # 开启在 map 端进行聚合,默认为 true |
Count(Distinct) 去重统计
数据量大的情况下,由于 COUNT DISTINCT
操作需要用一个Reduce Task来完成,这一个 Reduce 需要处理的数据量太大,就会导致整个Job很难完成,一般 COUNT DISTINCT
操作可以使用先 GROUP BY
再 COUNT
的方式替换:
1 | select count(distinct team_id) from player; |
笛卡尔积
尽量避免笛卡尔积,Hive只能使用1个reducer来完成笛卡尔积
行列过滤
列处理: 在 SELECT
中只拿需要的列,如果有分区尽量使用分区过滤,少用不用SELECT *
。
行处理: 在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在 Where
后面,那么就会先全表关联,之后再过滤。
1 | # 先关联两张表,再用 where 条件过滤 |
动态分区调整
动态分区(Dynamic Partition) 是指 对分区表 Insert 数据时候,数据库自动会根据分区字段的值将数据插入到相应的分区中。
配置动态分区:
1 | # 开启动态分区功能(默认true 开启) |
动态分区表要使用 insert 从一个已存在的表的基础上建立
1 | insert overwrite table log2 partition (day) |
合理使用分桶、分区
数据倾斜优化
Map 数
- 通常情况下,作业会通过input的目录产生一个或者多个map任务。主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小。
- Map 数不是越多越好,如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。这种情况要减少 map 数
- 对于接近 128M 的文件,正常会用一个 map 去完成,但如果这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。这种情况,应该增加 map 数
小文件进行合并减少 map 数
在 map 执行前合并小文件,减少 map 数。
CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。
1 | set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; |
复杂文件增加 map 数
当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。
增加 map 的方法:
1 | computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M |
案例:
1 | # 普通查询 |
Reduce数
reduce个数并不是越多越好,过多的启动和初始化reduce也会消耗时间和资源。
另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题。
在设置reduce个数的时候也需要考虑这两个原则:处理大数据量利用合适的reduce数;使单个reduce任务处理数据量大小要合适;
计算的方式调增 reduce 数
1 | # 每个 Reduce 处理的数据量默认是 256MB |
修改配置项调整 reduce 数
在 hadoop 的 mapred-default.xml 文件中修改,或在hive命令行中执行:
1 | set mapreduce.job.reduces=15; |
并行执行
Hive 会将一个查询转化成一个或者多个阶段。默认情况下,Hive 一次只会执行一个阶段。
不过,并非完全互相依赖的阶段是可以并行执行的,这样可能使得整个job的执行时间缩短。
1 | # 开启并发执行 |
严格模式
Hive 提供了一个严格模式,可以防止用户执行那些可能有不好的影响的查询。
1 | <property> |
开启严格模式后会禁止三种类型的查询:
对于分区表,如果 where 语句中没有分区字段过滤条件来限制范围,则不允许执行
这样是为了不让用户扫描所有分区,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗巨大资源。对于使用了order by语句的查询,要求必须使用limit语句
order by为了执行排序过程会将所有的结果数据分发到同一个Reducer中进行处理,强制要求用户增加 LIMIT 语句可以防止 Reducer 额外执行很长一段时间。限制笛卡尔积的查询
小弟出道多年,从未见过在数据库中做笛卡尔积
JVM 重用
JVM 重用对 Hive 的性能具有很大的影响,特别是对于小文件或 task 特别多的场景。
Hadoop 的默认配置通常是使用派生 JVM 来执行 map 和 Reduce 任务的。
这时 JVM 的启动过程可能会造成相当大的开销,尤其是执行的 job 包含有成百上千 task 任务的情况。
JVM重用可以使得 JVM 实例在同一个job中重新使用 N 次。
N 的值可以在Hadoop的mapred-site.xml文件中进行配置。通常在10-20之间,具体多少需要根据具体业务场景测试得出。
1 | <property> |
这个功能的缺点是,开启JVM重用将一直占用使用到的 task 插槽,以便进行重用,直到任务完成后才能释放。
如果某个“不平衡的”job中有某几个reduce task执行的时间要比其他Reduce task消耗的时间多的多的话,那么保留的插槽就会一直空闲着却无法被其他的job使用,直到所有的task都结束了才会释放。
推测执行
在分布式集群环境下,同一个作业的多个任务之间运行速度通常不一致,有些任务的运行速度可能明显慢于其他任务从而拖慢整体执行进度。
为了避免这种情况,Hadoop 采用了推测执行(Speculative Execution)机制,它根据一定的法则推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。
在 Hadoop 的 mapred-site.xml 中开启推测执行:
1 | <property> |
hive 也提供了配置项来控制 reduce-side 的推测执行:
1 | <property> |
如果用户对于运行时的偏差非常敏感的话,那么可以将这些功能关闭掉。
如果用户因为输入数据量很大而需要执行长时间的map或者Reduce task的话,那么启动推测执行造成的浪费是非常巨大大。
查看执行计划(EXPLAIN)
1 | EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query; |
压缩
文章标题:Hive 优化总结
文章字数:3.8k
本文作者:Waterandair
发布时间:2018-06-30, 18:19:32
最后更新:2019-12-28, 14:03:59
原始链接:https://waterandair.github.io/2018-06-30-hive-optimization.html版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。