[HIVE]Hive_on_tez常见问题汇总

603 阅读11分钟

引言

随着大数据技术的不断发展,Hive和Tez已经成为广泛使用的数据处理工具。Hive是一个数据仓库基础设施,它允许用户使用SQL语言进行数据查询和分析,而Tez则是一个用于处理大规模数据的分布式计算框架。在Hive中,使用Tez作为计算引擎可以有效地提高数据处理性能和效率。

然而,在实际使用过程中,Hive on Tez可能会出现一些常见问题,这些问题可能会影响数据处理和分析的顺利进行。因此,本文旨在汇总Hive on Tez使用过程中遇到的常见问题,并给出相应的解决方案,以便用户更好地使用Hive和Tez进行数据处理。相关问题及解决办法将会不定期更新。

问题1:作业跑批OutOfMemoryError

1.1 问题描述

一个每天正常运行的跑批任务某一天突然报错,报错信息如下: 6ca8fc544ade35acc0f4fb059bb6171.png

关键信息:

java.lang.OutOfMemoryError: Java heap space

1.2 原因分析

随着数据量的增长,作业运行所需的资源也会逐渐增长,当数据量达到一定程度时,数据量的增长无法满足作业的资源要求,需要将作业资源调大。另外需确认作业的AM是否也存在OOME的问题,如存在,则也需要调大AM的资源。

1.3 解决办法

调大作业内存参数:

  • hive.tez.container.size(单位:mb):定义Tez任务的容器大小,即每个Tez任务向YARN请求的资源量。它通常是指内存大小和CPU核心数。适当调整此参数可以提高Tez任务的性能和稳定性。
  • tez.runtime.unordered.output.buffer.size-mb(单位:mb):这个参数是用来设定Tez在内存缓冲区中保存未排序输出记录的最大空间。这个配置会影响执行完成后数据输出到磁盘的时间,如果设置的过小,那么数据可能会被频繁地写入到磁盘,这将增加I/O延时。如果设置的过大,数据会占用更多的内存,可能会增加GC(Garbage Collection, 垃圾回收)负载。
  • tez.runtime.io.sort.mb(单位:mb):这个参数设定了 Tez 运行排序操作时可用的最大内存。排序操作的内存大小也会影响到排序的效率和性能。如果设置的过小,可能会导致频繁的磁盘I/O操作,进而影响性能。如果设置的过大,可能会占用过多的内存,导致其它操作无法正常执行。

问题2:将外表删除时,对应的hdfs文件不会被删除

2.1 问题描述

建外表并插入数据

create external table test_external (id string) stored as parquet;
insert into test_external select '1';

删除外表

drop table test_external;

查看外表对应的目录及数据文件,发现删除外表之后仍然存在

hdfs://nameservice1/warehouse/test_external/000000_0

重建外表之后发现数据仍然存在:

create external table test_external (id string) stored as parquet;
select * from test_external;
返回1

2.2 原因分析

当前使用的是Hive3的版本,Hive3相比于Hive2,是有一些区别的,在HIVE中删除外表,默认情况下只删除了hive中的元数据信息,而不能将表对应的Hdfs底层目录及数据文件删除。

Hive2.X中DROP table时,对于内表,会删除 HMS 中的元数据和底层存储系统 HDFS 中的数据,而对于外表,则只会删除HMS中的元数据不会删除底层存储系统 HDFS 中的数据。

在Hive3.X中,根据DROP TABLE时是否删除底层存储系统 HDFS 中的数据,external 外表又被分为两种类型:external non purgeable table 和external purge table,前者在DROP TABLE 时跟 Hive2.X 的外表行为一致,不会删除底层HDFS中的数据。

而后者DROP TABLE时跟Hive2.X的内表行为一致,也会删除底层 HDFS 中的数据;Hive3.X 中创建外表时外表的具体类型,可以通过session级别参数hive.external.table.purge.default控制,也可以在建表时在table级别进行覆盖:

TBLPROPERTIES (‘EXTERNAL’=‘TRUE’, ‘external.table.purge’=’TRUE'/'FALSE')

关于Hive2.x和Hive3.x的区别,可参见:www.zhihu.com/question/51…

2.3 解决办法

将外表属性external.table.purge设置成true

在建表时执行表属性:

create external table test_external (id string) stored as parquet TBLPROPERTIES ('external.table.purge'='true');

对于已经建好的表,可以用alter table命令

alter table test_external set TBLPROPERTIES ('external.table.purge'='true');

(注意:该参数必须为小写,改成大写不生效)

参考链接:

issues.apache.org/jira/browse…

验证记录:

版本:3.1.3000.7.1.6.0-297 r20330ddfd644264f678a0456f374fb5dec58a940

创建两张表

create external table test_001 (id int);
create external table test_002 (id int) TBLPROPERTIES ('external.table.purge'='true');

查看对应的目录

# hadoop fs -ls /warehouse/test.db/

Found 2 items

drwxr-xr-x - hive hive 0 2022-10-13 14:12 /warehouse/test.db/test_001

drwxr-xr-x - hive hive 0 2022-10-13 14:12 /warehouse/test.db/test_002

删除两张表

drop table test_001;
drop table test_002;

查看对应目录:

# hadoop fs -ls /warehouse/test.db/ Found 1 items drwxr-xr-x - hive hive 0 2022-10-13 14:12 /warehouse/test.db/test_001

可以看出表test_002在加上TBLPROPERTIES ('external.table.purge'='true')属性之后,目录在删表的时候也连带着被删除了。

问题3:Hive3的textfile表指定多字符列分隔符

3.1 问题描述

当Hive的版本由2升到3之后,发现原本支持多字符分隔符的textfile表现在加载不出来数据了。

3.2 原因分析

textfile的多字符分隔符依赖于多字符解析的类,需要找到对应的类。

CDP 7.1.5以及更高版本里的多字符分割class MultiDelimitSerDe已经从之前的org.apache.hadoop.hive.contrib.serde2.MultiDelimitSerDe

改成了org.apache.hadoop.hive.serde2.MultiDelimitSerDe

在建表时,注意类名的变化。

3.3 解决办法

指定新的序列化类来创建textfile表:

create table test (id string)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.MultiDelimitSerDe'  
WITH SERDEPROPERTIES ("field.delim"="@!@") STORED AS TEXTFILE;

值得注意的是,这里仅支持多字符的列分隔符,虽然在建标时也有行分隔符的属性,但在实测中发现指定了行分隔符是不生效的。

问题4:查询大表报错Bucket ID out of range: -1

4.1 问题描述

在hive3里面查询一个大的分区表,报错:

Error: java.io.IOException: java.lang.IllegalArgumentException: Bucket ID out of range: -1 (state=,code=0)

查询语句: select * from big_table where day = '2022-01-01' limit 10;

其中,big_table为一个十亿级的大表。

4.2 原因分析

在tez中查询大表时,由于hive.fetch.task.conversion参数的存在,无法不通过tez作业直接拿到大表的数据,需要把优化机制关掉。

hive.fetch.task.conversion参数的作用是控制查询执行过程中的数据获取方式,该参数有以下几个取值:

  • NONE:表示禁用数据获取优化,查询将按照默认方式执行。
  • MORE:表示启用数据获取优化,对于小规模的查询,Hive将尝试在单个任务中获取所有数据,以减少数据传输和任务数量。
  • DEFAULT:表示使用Hive的默认设置,根据查询的复杂性和数据规模来决定是否进行数据获取优化。

4.3 解决办法

set hive.fetch.task.conversion=none;

问题5:Hive建表不支持中文字段名

5.1 问题描述

hive3建表时中文名显示乱码 1695524069(1).png

在常规数仓中,hive的表名和字段名一般为全英文且要按一定的标准化来命名,但一些数据同步的场景下,需要把表名或者字段名建成中文名,但在hive中建表时,中文名会显示乱码,如何让hive支持中文的表名呢。

5.2 原因分析

hive的元数据都是存在hive metastore中,对应的是Mysql、Postgre SQL为代表的关系型数据库,如果发现表名不支持中文,要从关系型数据库的中文支持入手。

登录hive-metastore后台mysql,找到列属性对应的表:

use metastore;
show create table column_v2;

 CREATE TABLE `columns_v2` (
  `CD_ID` bigint(20) NOT NULL,
  `COMMENT` varchar(256) CHARACTER SET utf8 DEFAULT NULL,
  `COLUMN_NAME` varchar(767) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
  `TYPE_NAME` mediumtext,
  `INTEGER_IDX` int(11) NOT NULL,
  PRIMARY KEY (`CD_ID`,`COLUMN_NAME`),
  KEY `COLUMNS_V2_N49` (`CD_ID`),
  CONSTRAINT `COLUMNS_V2_FK1` FOREIGN KEY (`CD_ID`) REFERENCES `cds` (`CD_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

COLUMN_NAME的编码格式为latin1,不是中文常用的utf8格式。修改column_v2的字段名编码格式,即可让列名支持中文名,表名的中文支持同理,找到表名所在的表即可,生产环境操作metastore存在较大的风险,在操作之前切记做好数据库的备份。

5.3 解决办法

修改步骤:

  1. 将hive-metastore服务停掉
  2. 备份COLUMNS_V2,在mysql本地执行: mysqldump -uroot -p metastore COLUMNS_V2 > /data/COLUMNS_V2
  3. 登录mysql,修改编码格式 alter table COLUMNS_V2 modify column COLUMN_NAME varchar(767) character set utf8;
  4. 启动hive-metastore
  5. 验证,问题解决 1695526035(1).png

问题6:一些union all操作会产生子目录

6.1 问题描述

一些通过union all产生的表中,对应的hdfs路径会带一个子目录。

create table test(id string) stored as parquet tblproperties ('transactional' = 'false');

insert into test select '1' union all select '2' union all select '3';

此时可看底层目录中多了HIVE_UNION_SUBDIR目录

/warehouse/test.db/test/HIVE_UNION_SUBDIR_0/000000_0 /warehouse/test.db/test/HIVE_UNION_SUBDIR_1/000000_0 /warehouse/test.db/test/HIVE_UNION_SUBDIR_2/000000_0

影响: 通过distcp将hive3的表同步到hive2的时候数据会加载不出来。

6.2 原因分析

CDP在动态分区的情况下写入表时用UNION或UNION ALL插入ORC表时会引入一个BUG,CDP在修复这个BUG的时候将

hive.merge.tezfile=true参数改为false了,引入了新的目录

相关链接:bug-fix HIVE-17275

issues.apache.org/jira/browse…

6.3 解决办法

【解决办法】 由于这个问题是解决bug的过程中引入的,当数据只在hive3中流转时,出现子目录并不影响hive3的,因此官方并不认为这是一个新的bug,因此智能通过新增一些参数来规避子目录的出现。

1.新增过程参数:

针对分区表:set hive.exec.dynamic.partition.mode = nonstrict;

针对非分区表:set hive.merge.tezfiles=true;

my.cloudera.com/knowledge/U…

(注意:设置以上两个参数无法保证生成的结果里面无子目录的情况,需要By Case去验证)

修改以上的参数不能保证完全解决问题, 针对非分区表,还可以添加以下两个参数进一步降低出现子目录的可能性:

对于union all插入非分区表可以设置:

set hive.merge.size.per.task=268435456;

set hive.merge.smallfiles.avgsize=268435456;

这将有助于更频繁地触发tez合并,tez合并时会将子目录中的数据文件合并成一个大文件,减少小文件的产生,但不能保证它始终可以删除子目录。

2.采用数据准备完毕后作 insert overwrite来解决该问题,可规避该问题,但会带来额外的计算资源开销。

insert overwrite table test select * from test;

3.若Hive2仍然用MR作为计算引擎,则可以在hive3同步hive2之后,在hive2端读取数据时添加MR相关参数:

set mapred.input.dir.recursive=true; (在起MR时生效)

set hive.mapred.supports.subdirectories=true;(读数据时生效)

关于上述几个参数的说明:

以下是解释所提到参数的含义:

hive.exec.dynamic.partition.mode:用于指定Hive在执行动态分区插入时的模式。它有三个可选值:

  • strict:严格模式,必须指定动态分区键的值。
  • nonstrict:非严格模式,如果动态分区键的值未指定,Hive将默认使用NULL值。
  • none:禁用动态分区插入。

hive.merge.tezfiles:这个参数在Hive使用Tez作为执行引擎时生效,用于指定是否开启合并Tez文件的操作。当设置为true时,Hive会尝试合并Tez任务输出的小文件,以减少文件的数量和提高性能。

hive.merge.size.per.task:这个参数指定了Hive在合并任务输出文件时的目标合并文件大小。它表示每个任务合并后的文件大小(以字节为单位)。这个参数可以帮助优化合并任务的性能和文件数量。

hive.merge.smallfiles.avgsize:这个参数用于设置平均小文件大小的阈值。当一个目录中的文件平均大小小于该阈值时,Hive会尝试合并这些小文件。它可以帮助优化小文件合并的效率。

mapred.input.dir.recursive:在Hive使用MR作为执行引擎时生效,指定了Hadoop MapReduce任务执行输入时是否递归地读取所有子目录。当设置为true时,MapReduce任务将递归地读取输入目录下的所有子目录。

hive.mapred.supports.subdirectories:在Hive使用MR作为执行引擎时生效,用于指定Hive执行MapReduce任务时是否支持读取子目录。当设置为true时,Hive可以在MapReduce任务中读取子目录。