hive-6 常见问题处理

1,297 阅读22分钟

1. 内存溢出

1.1 Client端内存溢出

Client端发生内存溢出执行下面的看是很简单的一条sql语句: hive> select count(1) from test_tb_1_1;

Query ID = hdfs_20180802104347_615d0836-cf41-475d-9bec-c62a1f408b21
Total jobs = 1
Launching Job 1 out of 1
Number of reduce tasks determined at compile time: 1
In order to change the average load for a reducer (in bytes):
set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
set mapreduce.job.reduces=<number>
FAILED: Execution Error, return code -101 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask. Java heap space

报错原因: 该语句会进行全表全分区扫描,如果该表的分区数很多,数据量很大,可能就会出现客户端内存不足的报错。 注:客户端报内存溢出的判断依据,通过查看客户端输出来的日志中,还没有打印出作业的application id 信息(信息样式如下)就报内存溢出的异常了,在ResourceManager上也查看不到该作业的任何信息。

由于是客户端,在启动hive的时候就要指定好参数,启动之后修改不了,因此需要在启动hive命令之前,先修改环境变量 export HIVE_CLIENT_OPTS="-Xmx1536m -XX:MaxDirectMemorySize=512m" 默认值是 1.5G , 可以根据需要调大一些。 例如: export HIVE_CLIENT_OPTS="-Xmx2g -XX:MaxDirectMemorySize=512m"

1.2 ApplicationMaster端内存溢出

为了演示,先将am的内存调小到 512M。 之后客户端输入如下的报错信息:

set yarn.app.mapreduce.am.resource.mb=512; select count(1) from (select num, count(1) from default.test_tb_1_1 where part>180 and part<190 group by num) a;

Query ID = hdfs_20180802155500_744b90db-8a64-460f-a5cc-1428ae61931b
Total jobs = 2
Launching Job 1 out of 2
Number of reduce tasks not specified. Estimated from input data size: 849
In order to change the average load for a reducer (in bytes):
set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
set mapreduce.job.reduces=<number>
Starting Job = job_1533068988045_266336, Tracking URL = http://xxx:8088/proxy/application_1533068988045_266336/
Kill Command = hadoop job -kill job_1533068988045_266336
Hadoop job information for Stage-1: number of mappers: 0; number of reducers: 0
2018-08-02 15:55:48,910 Stage-1 map = 0%, reduce = 0%
Ended Job = job_1533068988045_266336 with errors
Error during job, obtaining debugging information...
FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask
MapReduce Jobs Launched:
Stage-Stage-1: HDFS Read: 0 HDFS Write: 0 FAIL
Total MapReduce CPU Time Spent: 0 msec

从报错信息中可以看出有application id 信息,说明任务提交到了ResourceManager上,不是client端导致的,但是有没有看到container容器失败的信息,直接0%后就失败,初步可以判断是由于application master内存不足导致的,进一步确定需要两种途径都行:

1)在ResourceManager的界面查看程序的运行信息,可以看到如下的报错信息:

Current usage: 348.2 MB of 512 MB physical memory used; 5.2 GB of 1.5 GB virtual memory used. Killing container.

说明内存溢出了,Application application_1533068988045_266336 failed 2 times due to AM Container for appattempt_1533068988045_266336_000002 exited with exitCode: -103 可以确定是application master发生的内存溢出。

2)可以使用yarn的命令来获取日志信息

yarn logs -applicationId application_1533068988045_266336 > application_1533068988045_266336.log 该日志信息中的数据和上面的一样,判断方式相同。注:使用该命令要注意日志文件很大的作业,因此最好不在系统盘做这件事。 解决方法: application master 内存溢出设置下面的参数(单位MB)

set yarn.app.mapreduce.am.resource.mb=4096;
set yarn.app.mapreduce.am.command-opts=-Xmx3276M; #注:java.opts是memory.mb的80%左右
set yarn.app.mapreduce.am.resource.cpu-vcores=8; #--MR ApplicationMaster占用的虚拟CPU个数

Map端和Reduce端内存溢出

Map端 和 Reduce端出现内存溢出的标志:

首先会看到提示container内存不足的信息(例如:beyond physical memory):

Container [pid=192742,containerID=container_e1383_1533068988045_250824_01_003156] is running beyond physical memory limits. Current usage: 2.0 GB of 2 GB physical memory used; 3.7 GB of 6.2 GB virtual memory used. Killing container.

通过日志信息中attempt信息 attempt_1533068988045_250824_r_000894_3 中间的 r 和 m 可以看出是map还是reduce容器挂了。 此处的错误日志是reduce容器挂了。

Task with the most failures(4):
Task ID:task_1533068988045_250824_r_000894
URL:
http://0.0.0.0:8088/taskdetails.jsp?jobid=job_1533068988045_250824&tipid=task_1533068988045_250824_r_000894
Diagnostic Messages for this Task:
Container [pid=192742,containerID=container_e1383_1533068988045_250824_01_003156] is running beyond physical memory limits. Current usage: 2.0 GB of 2 GB physical memory used; 3.7 GB of 6.2 GB virtual memory used. Killing container.

解决方式:在执行sql语句前 修改如下的参数(单位MB):

Map容器内存溢出设置下面的参数

set mapreduce.map.memory.mb=4096;
set mapreduce.map.java.opts=-Xmx3276M;

Reduce容器内存溢出设置下面的参数

set mapreduce.reduce.memory.mb=4096;
set mapreduce.reduce.java.opts=-Xmx3276M;
注:java.opts是memory.mb的80%左右根据内存溢出时提示的内存大小
(例如:Current usage: 2.0 GB of 2 GB physical memory used)进行增大内存,增大的最小单位是1GB。

参数介绍:

  • mapreduce.map.memory.mb: 一个Map Task可使用的内存上限(单位:MB),默认为 1024。如果 Map Task实际使用的资源量超过该值,则会被强制杀死;
  • mapreduce.map.java.opts: Map Task 的 JVM 参数,你可以在此配置默认的java heap size参数,一般设置成上一个参数的70%-80%.
  • mapreduce.reduce.memory.mb: 一个 Reduce Task 可使用的资源上限(单位:MB),默认为1024。如果Reduce Task实际使用的资源量超过该值,则会被强制杀死;
  • mapreduce.reduce.java.opts: Reduce Task 的 JVM 参数,你可以在此配置默认的java heap size参数,一般设置成上一个参数的70%-80%.
  • mapreduce.map.cpu.vcores
  • mapreduce.reduce.cpu.vcores

2.数据倾斜

简单来说数据倾斜就是数据的key 的分化严重不均,造成一部分数据很多,一部分数据很少的局面。

举个 word count 的入门例子,它的map 阶段就是形成 (“aaa”,1)的形式,然后在reduce 阶段进行 value 相加,得出 “aaa” 出现的次数。若进行 word count 的文本有100G,其中 80G 全部是 “aaa” 剩下 20G 是其余单词,那就会形成 80G 的数据量交给一个 reduce 进行相加,其余 20G 根据 key 不同分散到不同 reduce 进行相加的情况。如此就造成了数据倾斜,临床反应就是 reduce 跑到 99%然后一直在原地等着 那80G 的reduce 跑完。

image.png

2.1 Join中的计算均衡优化

在hive中,join操作一般都是在reduce阶段完成的,写sql的时候要注意把小表放在join的左边,原因是在 Join 操作的 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存,将条目少的表放在左边,可以有效减少发生 out of memory 错误的几率。 一个大表和一个配置表的reduce join经常会引起计算不均衡的情况。

select user.id,gender_config.gender_id
from gender_config 
join user on gender_config.gender=user.gender
---配置表gender_config(gender string,gender_id int)

image.png

gender 只有男女两个值,hive处理join的时候把join_key作为reduce_key,因此会出现和group by类似的reduce计算不均衡现象,只有两个reduce参与计算,每个reduce计算100亿条记录。

select /*+ MAPJOIN(gender_config) */ user.id, gender_config.gender_id 
from gender_config 
join user on gender_config.gender=user.gender

image.png

这种大表和配置表通常采用mapjoin的方式来解决这种不均衡的现象。目前hive是采用/*+ MAPJOIN(gender_config) */提示的方式告诉翻译器把sql翻译成mapjoin,提示里必须指明配置表是哪个。 每个map会把小表读到hash table,然后和大表做hash join。因此map join的关键是小表能放入map进程的内存,如果内存放不下会序列化到硬盘,效率会直线下降

成千上万个map从hdfs读这个小表进自己的内存,使得小表的读操作变成成个join的瓶颈,甚至有些时候有些map读这个小表会失败(因为同时有太多进程读了),最后导致join失败。临时解决办法是增加小表的副本个数。下一步优化可以考虑把小表放入Distributed Cache里,map读本地文件即可。

参数

  1. mapjoin
  • set hive.auto.convert.join=true; 根据输入文件的大小决定是否将普通join转换为mapjoin的一种优化,默认不开启false;
  • set hive.mapjoin.smalltable.filesize=50000000; # The threshold for the input file size of the small tables; if the file size is smaller than # this threshold, it will try to convert the common join into map join
  1. Skew Join
  • set hive.optimize.skewjoin=true;--如果是join 过程出现倾斜 应该设置为true
  • set hive.skewjoin.key=100000; --这个是join的键对应的记录条数超过这个值则会进行分拆,值根据具体数据量设置。 hive在运行的时候没有办法判断哪个key会产生多大的倾斜,所以使用这个参数控制倾斜的阈值,如果超过这个值,新的值会发送给那些还没有达到的reduce, 一般可以设置成(处理的总记录数/reduce个数)的2-4倍都可以接受.

场景

1》大表和小表关联

比如,一个上千万行的记录表和一个几千行表之间join关联时,容易发生数据倾斜。为什么大表和小表容易发生数据倾斜(无非有的reduce执行时间被延迟)?可参考Hive中小表与大表关联(join)的性能分析

解决方式:

  1. 多表关联时,将小表(关联键记录少的表)依次放到前面,这样可以触发reduce端更少的操作次数,减少运行时间。
  2. 同时可以使用Map Join让小的维度表缓存到内存。在map端完成join过程,从而省略掉reduce端的工作。但是使用这个功能,需要开启map-side join的设置属性:set hive.auto.convert.join=true(默认是false)。同时还可以设置使用这个优化的小表的大小:set hive.mapjoin.smalltable.filesize=25000000(默认值25M)
  3. Mapjoin,在map阶段完成join,不需要reduce. 
 Select /*+ MAPJOIN(b) */
            a.key, a.value from a join b on a.key = b.key
2》大表和大表的关联

1)比如:大表和大表关联,但是其中一张表的多是空值或者0比较多,容易shuffle给一个reduce,造成运行慢。

解决方法:把空置变成一个字符串加上随机数,把倾斜的数据分布到不同的reduce上

select * from log a
left outer join users b
on case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;
3》避免不同数据类型关联

场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。

可以考虑把数据类型转化为同一数据类型

select * from user a 
left outer join log b
on a.user_id=cast(b.user_id as string);
4》避免多对多关联

在join链接查询时,确认是否存在多对多的关联,起码保证有一个表的结果集的关联字段不重复

5》避免笛卡尔乘积

在join链接查询中没有on链接,而通过where条件语句会产生笛卡尔集

问题: Select a.key, b.value from A a join B b where a.key=b.key
优化:Select a.key, b.value from A a join B b on a.key=b.key

2.2 Group By中的计算均衡优化

Map端部分聚合

hive.map.aggr
select user.gender,count(1) from user group by user.gende;

image.png

如果没有map端的部分聚合优化,map直接把groupby_key 当作reduce_key发送给reduce做聚合,就会导致计算不均衡的现象。虽然map有100万个,但是reduce只有两个在做聚合,每个reduce处理100亿条记录。 image.png

set hive.map.aggr=true

这个参数默认是打开的, 在group by的时候是否map局部聚合。参数打开后的计算过程如上图。由于map端已经做了局部聚合,虽然还是只有两个reduce做最后的聚合,但是每个reduce只用处理100万行记录,相对优化前的100亿小了1万倍

map聚合开关缺省是打开的,但是不是所有的聚合都需要这个优化。考虑先面的sql,如果groupby_key是用户ID,因为用户ID没有重复的,因此map聚合没有太大意义,并且浪费资源。

set hive.groupby.mapaggr.checkinterval = 100000;
set Hive.map.aggr.hash.min.reduction=0.5;
select user..id,count(1) from user group by user.id;

上面这两个参数控制map聚合的关闭策略。Map开始的时候先尝试给前100000条记录做hash聚合,如果聚合后的记录数/100000>0.5说明这个groupby_key没有什么重复的,再继续做局部聚合没有意义,100000 以后就自动把聚合开关关掉。

在map的log中会看到下面的提示:

2011-02-23 06:46:11,206 WARN org.apache.hadoop.hive.ql.exec.GroupByOperator: Disable Hash Aggr: #hash table = 99999 #total = 100000 reduction = 0.0 minReduction = 0.52.
hive.groupby.skewindata
select user.gender,count(distinct user.id) from user group by user.gender;

image.png

通常数据倾斜都是在有distinct出现的时候,比如上面的sql,由于map需要保存所有的user.id,map聚合开关会自动关掉,导致出现计算不均衡的现象,只有2个redcue做聚合,每个reduce处理100亿条记录。

set hive.groupby.skewindata =true;

参数会把上面的sql翻译成两个MR

image.png

第一个MR的reduce_key是gender+id。因为id是一个随机散列的值,因此这个MR的reduce计算是很均匀的,reduce完成局部聚合的工作

image.png

第二个MR完成最终的聚合,统计男女的distinct id值,每个Map只输出两条记录,因此虽然只有两个redcue计算也没有关系,绝大部分计算量已经在第一个MR完成

select id,count (distinct gender) from user group by user.id

hive.groupby.skewindata默认是关闭的,因此如果确定有不均衡的情况,需要手动打开这个开关。当然,并不是所有的有distinct的group by都需要打开这个开关,比如上面的sql。因为user.id是一个散列的值,因此已经是计算均衡的了,所有的reduce都会均匀计算。只有在groupby_key不散列,而distinct_key散列的情况下才需要打开这个开关,其他的情况map聚合优化就足矣。

2.3 count distinct

如果数据量非常大,执行如select a,count(distinct b) from t group by a;类型的SQL时,会出现数据倾斜的问题。

解决方式:使用sum...group by代替。如select a,sum(1) from (select a,b from t group by a,b) group by a;

尽量避免使用distinct 进行排重,特别是大表操作,用group by 代替

优化前:Select count (distinct key) from a

优化后:Select sum(1) from (Select key from a group by key) t

3.调整map/reduce个数

3.1 控制hive任务中的map数

  • 通常情况下,作业会通过input的目录产生一个或者多个map任务。

主要的决定因素有: input的文件总个数,input的文件大小,集群设置的文件块大小(目前为128M, 可在hive中通过set dfs.block.size;命令查看到,该参数不能自定义修改);

举例:

a) 假设input目录下有1个文件a,大小为780M,那么hadoop会将该文件a分隔成7个块(6个128m的块和1个12m的块),从而产生7个map数

b) 假设input目录下有3个文件a,b,c,大小分别为10m,20m,130m,那么hadoop会分隔成4个块(10m,20m,128m,2m),从而产生4个map数 即,如果文件大于块大小(128m),那么会拆分,如果小于块大小,则把该文件当成一个块。

  • 是不是map数越多越好? 答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。

  • 是不是保证每个map处理接近128m的文件块,就高枕无忧了? 答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

针对上面的问题3和4,我们需要采取两种方式来解决:即减少map数和增加map数;

3.1.1 如何合并小文件,减少map数?

假设一个SQL任务:Select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’; 该任务的inputdir /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04共有194个文件,其中很多是远远小于128m的小文件,总大小9G,正常执行会用194个map任务。 Map总共消耗的计算资源: SLOTS_MILLIS_MAPS= 623,020

我通过以下方法来在map执行前合并小文件,减少map数:

set hive.hadoop.supports.splittable.combineinputformat = true;
set mapreduce.input.fileinputformat.split.maxsize=100000000;---每个Mapper任务处理的数据量的最大值100M. (这个值决定了合并后文件的大小)
set mapreduce.input.fileinputformat.split.minsize.per.node=100000000;---一个节点上split片的最小值(这个值决定了多个DataNode上的文件是否需要合并)
set mapreduce.input.fileinputformat.split.minsize.per.rack=100000000;---一个交换机下split片的最小值(这个值决定了多个交换机上的文件是否需要合并)
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

再执行上面的语句,用了74个map任务,map消耗的计算资源:SLOTS_MILLIS_MAPS= 333,500

对于这个简单SQL任务,执行时间上可能差不多,但节省了一半的计算资源。

大概解释一下,100000000表示100M, set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;这个参数表示执行前进行小文件合并, 前面三个参数确定合并文件块的大小,大于文件块大小128m的,按照128m来分隔,小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分隔大文件剩下的), 进行合并,最终生成了74个块。

3.1.2 如何适当的增加map数?

当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。 假设有这样一个任务:

 Select data_desc,
        count(1),
        count(distinct id),
        sum(case when …),
        sum(case when ...),
        sum(…)
from a group by data_desc

如果表a只有一个文件,大小为120M,但包含几千万的记录,如果用1个map去完成这个任务,肯定是比较耗时的,这种情况下,我们要考虑将这一个文件合理的拆分成多个, 这样就可以用多个map任务去完成。

set mapreduce.job.reduces=10;
create table a_1 as 
select * from a 
distribute by rand(123); 

这样会将a表的记录,随机的分散到包含10个文件的a_1表中,再用a_1代替上面sql中的a表,则会用10个map任务去完成。

每个map任务处理大于12M(几百万记录)的数据,效率肯定会好很多。

看上去,貌似这两种有些矛盾,一个是要合并小文件,一个是要把大文件拆成小文件,这点正是重点需要关注的地方, 根据实际情况,控制map数量需要遵循两个原则:使大数据量利用合适的map数;使单个map任务处理合适的数据量;

3.2 控制hive任务的reduce数

3.2.1 Hive自己如何确定reduce数:

reduce个数的设定极大影响任务执行效率,不指定reduce个数的情况下,Hive会猜测确定一个reduce个数,基于以下两个设定:

hive.exec.reducers.bytes.per.reducer(每个reduce任务处理的数据量,默认为1000^3=1G)
hive.exec.reducers.max(每个任务最大的reduce数,默认为1009

如果用户不主动设置mapred.reduce.tasks数,则会根据input directory计算出所有读入文件的input summary size,然后除以这个值算出reduce number

   reducers = (int) ((totalInputFileSize + bytesPerReducer - 1) / bytesPerReducer);
   reducers = Math.max(1, reducers);
   reducers = Math.min(maxReducers, reducers);
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;
---/group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04 总大小为9G多,因此这句有10个reduce

reduce个数并不是越多越好;

同map一样,启动和初始化reduce也会消耗时间和资源;

另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

3.2.2 调整reduce个数:

方法一: 调整hive.exec.reducers.bytes.per.reducer参数的值;

set hive.exec.reducers.bytes.per.reducer=500000000; (500M)
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt; 这次有20个reduce

方法二: 调整reduce个数mapreduce.job.reduces;

set mapreduce.job.reduces = 15;
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;这次有15个reduce

3.2.3 什么情况下只有一个reduce;

很多时候你会发现任务中不管数据量多大,不管你有没有设置调整reduce个数的参数,任务中一直都只有一个reduce任务;

其实只有一个reduce任务的情况,除了数据量小于hive.exec.reducers.bytes.per.reducer参数值的情况外,还有以下原因:

a) 没有group by的汇总

比如把select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt; 写成 select count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04'; 这点非常常见,希望大家尽量改写。

b) 用了Order by

c) 有笛卡尔积

通常这些情况下,除了找办法来变通和避免,我暂时没有什么好的办法,因为这些操作都是全局的,所以hadoop不得不用一个reduce去完成; 同样的,在设置reduce个数的时候也需要考虑这两个原则:使大数据量利用合适的reduce数;使单个reduce任务处理合适的数据量;

4.合并小文件

4.1 map输入合并

同3.1如何合并小文件,减少map数

4.1 自动合并输出的小文件

set hive.mergejob.maponly=true (默认为true)
-- 如果hadoop版本支持CombineFileInputFormat,则启动Map-only job for merge,否则启动  MapReduce merge job,map端combine file是比较高效的做法

set hive.merge.mapfiles=true (默认为true)
-- 正常的map-only job后,是否启动merge job来合并map端输出的结果

set hive.merge.mapredfiles=true (默认为false)
-- 正常的map-reduce job后,是否启动merge job来合并reduce端输出的结果,建议开启

set hive.merge.smallfiles.avgsize (默认为16MB)
-- 如果不是partitioned table的话,输出table文件的平均大小小于这个值,启动merge job,如果是partitioned table,则分别计算每个partition下文件平均大小,只merge平均大小小于这个值的partition。这个值只有当hive.merge.mapfiles或hive.merge.mapredfiles设定为true时,才有效

set hive.merge.size.per.task(默认是256MB)
-- merge job后每个文件的目标大小(targetSize),用之前job输出文件的total size除以这个值,就可以决定merge job的reduce数目。merge job的map端相当于identity map,然后shuffle到reduce,每个reduce dump一个文件,通过这种方式控制文件的数量和大小

5. hive tez insert union all问题

问题描述

在hive中使用tez模式时,发现tez的输出结果在对应表目录中,生成了子目录,造成未配置tez的hive客户端对该表进行读取时,无法获取到数据。

检查表的输出目录,在分区目录下发现了两个子目录:1和2:

/user/hive/test1/20170920000000/1
/user/hive/test1/20170920000000/2

原因

查看对应的sql,发现存在insert union操作,查看往上信息,发现tez对于insert union操作会进行优化,通过并行加快速度,为防止有相同文件输出,所以对并行的输出各自生成成了一个子目录,在子目录中存放结果。如果此时全部hive客户端引擎及相关都设定为tez,则无问题。如果有客户端还在使用mr引擎,则会出现读取不到数据的情况。而在hive中,对于表目录存放信息有如下策略:

在Hive默认设置中,hive只能读取对应表目录下的文件,

1) 如果表目录中同时存在目录和文件

则使用hive进行读取时,会报目录非文件的错误

2) 表目录中只有目录

hive不会进行深层递归查询,只会读取到对应查询目录,会查询结果为空

3)表目录中只有文件

可以正常查询

Hive on tez 中对insert union操作进行了优化,会并行输出到对应的表目录,为防止有相同名文件存在,所以为各自输出在表目录下各自设置了一个目录,里边存放执行结果

此种目录,对tez引擎客户端可读。但是对于mr引擎,因为其只会便遍历到对应分区层。发现分区下没有文件后,就返回空

解决思路

可以开启mapreduce的递归查询模式: set mapreduce.input.fileinputformat.input.dir.recursive=true

在hive中,设置执行查询后,会提示错误,要求将set hive.mapred.supports.subdirectories=true; 经过两个set后,仔执行查询,hive即可对tez引擎产union产出的数据进行访问。

同时为了查看tez的此种情况对其他引擎的兼容性,又对sparktThriftServer的sql访问进行了测试:

对于sparkthriftserver,只需添加set mapreduce.input.fileinputformat.input.dir.recursive=true ,即可访问对应数据 总结一下sparkThriftServer模式与hive的不同

1) 在表目录下同时有文件和目录时(非分区目录)

此时sparkThriftServer和hive都会报非文件错误

2) 在表目录下均为目录(非分区目录)

此时sparkThriftServer会报非文件错误,hive无错,但是查询为空

解决方案

1)方案一:所有hive端都替换为tez引擎

这不会出现上述问题,但会存在对其他引擎如spqrk,mr,presto等的影响

2)方案二:为hadoop和hive设置可递归读

该方案相对可行,但需要调研对普通mr任务的影响