ORC表或TORC表修改字段类型的排查方法以及解决方案

  其他常见问题
内容纲要

概要描述

本文描述TDH521之前早期版本没有禁止orc表torc表修改字段类型时带来的问题以及解决方案。
本文以TDH512为例,描述出现该场景的解决方法

详细描述

1 原理介绍

对ORC类型的存储,目前Inceptor中没有很好的支持类型间的转换。这是因为ORC格式是一种self-description的存储格式,在写入ORC文件时,会将字段类型一并写入文件。当读该ORC文件时,就可以通过文件中的类型,直接恢复出存储的数据。

如果修改ORC表或ORC事务表字段类型,那么会存在问题:在修改字段之前存储的数据,会按照ORC中报错的类型解析并生成struct。但由于该字段被修改了,所以最终在Inceptor会按照新的类型来解释该值,这期间会强制将旧的类型转换新类型,造成ClassCastException

2 TDH521之后,该功能已经被关闭。

TDH521之后,如果尝试修改字段类型会出现提示报错,且语句无法执行。
报错内容如下:
file

3 复现问题场景

orc事务表复现语句
CREATE TABLE info (id int ,name string ,age int)
clustered BY (id) INTO 3 buckets stored AS orc_transaction;

INSERT INTO info values(1,'李现',22);
INSERT INTO info values(2,'陈伟霆',28);
INSERT INTO info values(3,'朱一龙',30);
INSERT INTO info values(4,'许光汉',24);
INSERT INTO info values(5,'周杰伦',39);
INSERT INTO info values(6,'木村拓哉',45);

ALTER TABLE info change name name char(20);

此时查询该表,只要涉及被修改的字段

select * from info;

会出现类似如下报错:
org.apache.hadoop.hive.sql.metadata.HiveException:org.apache.hadoop.io.Text cannot be cast to org.apache.hadoop.hive.serde2.io.HiveCharWritable
file

orc表复现语句
create table info_orc(id int, name STRING,age int) stored as orc; 

insert into info_orc select 1,'李现',22 from system.dual;
insert into info_orc select 2,'陈伟霆',28 from system.dual;
insert into info_orc select 3,'朱一龙',30 from system.dual;
insert into info_orc select 4,'许光汉',24 from system.dual;
insert into info_orc select 5,'周杰伦',39 from system.dual;
insert into info_orc select 6,'木村拓哉',45 from system.dual;
alter table info_orc change name name char(20);

问题场景和orc事务表相似

4 问题影响

扫描表的相关操作都会手动影响,如果读到了该ORC文件中的对应字段,就会抛异常。但是此时insert等操作不涉及全表扫描的,仍然是可以进行的。
compaction的操作会扫描全表,会导致ORC事务表的compaction失败

5 问题排查

主要是找到ORC文件中字段类型与metastore中类型不匹配的地方

1 查看表的当前字段类型
desc info;

file

可以看到当前一共三个字段的记录,分别是:
id 对应 int类型
name 对应 char(20)类型
age 对应 int类型

2 使用orcfiledump工具,查看orc文件中的类型

orcfiledump工具可以查看某个ORC文件(注意是文件而不是目录)底层存储信息,其中就包括了字段类型信息
(以下命名需要访问hdfs文件,需要有权限的用户才可以操作)

1 4.x版本使用orcfiledump
/usr/lib/hive/bin/hive --service orcfiledump hdfs://xxx
2 5.x 版本使用orcfiledump
TDH-Client/inceptor/bin/hive --service orcfiledump hdfs://xxx

以info这张ORC事务表为例,进行查询

show create table info;

找到该表在hdfs上对应的location
file

hdfs上找到对应的location下的文件信息,在hdfs上找到相应的底层存储的文件(而不是文件夹),任选其中的一个文件
file
使用orcfiledump查看底层存储信息

/root/TDH-Client/inceptor/bin/hive --service orcfiledump /inceptor1/user/hive/warehouse/xiannv.db/hive/info/delta_0000016_0000016/bucket_00001

file

关注type这一行的显示信息,事务表Type对应行中row: struct<>内的字段名和类型即对应表的字段名和类型(同一base或delta目录下,所有buckets内的类型肯定一致)

ORC表的orcfiledump出来如下
file

6 数据恢复

按照之前的尝试,涉及全表扫描的操作都会有报错。如果需要恢复数据,就必须让ORC文件中的类型和metastore中的保持一致。

普通ORC表

因为普通ORC表没有CRUD操作,所以所有的文件都是append操作,因此,只要按类型分批导出即可。

为什么说要分批

是由于修改了字段类型之后,虽然不能进行全表扫描,但是insert等不涉及全表扫描的操作仍然是可以进行的。
所以以上述ORC表为例,此时插入一条

INSERT INTO info_orc SELECT 7,'岳云鹏',32 from system.dual;

可以查看到orc文件中的存储类型如下
file
及,该表的location下的数据类型,不仅有id int,name string,age int 类型的存储文件,还有id int,name char(20),age int类型的存储文件

导出的步骤

创建一个目标表,匹配修改后的表的类型,比如:

CREATE TABLE info_orc_t (id int, name CHAR(20),age int) stored as orc;

将orc文件存储类型为id int,name char(20),age int的文件导出到新建表的hdfs的location上

hadoop dfs -mv /inceptor1/user/hive/warehouse/xiannv.db/hive/info_orc/000000_0_copy_6 /inceptor1/user/hive/warehouse/xiannv.db/hive/info_orc_t

file
其他只剩下存储类型为id int,name string,age int的数据了。修改字段类型,从char(20)改为string

ALTER TABLE info_orc CHANGE name name STRING;

file

将数据并入新表info_orc_t中

INSERT INTO info_orc_t SELECT * FROM info_orc;

file

最终使得新表中导入了所有的数据,且字段类型为char(20)
有必要的话,drop原始表,并rename临时表为原始表,恢复原始表数据访问

ORC事务表

如果ORC事务表修改了类型,但没有做任何CRUD操作,可以直接将该字段恢复为修改前类型。
如果ORC事务表改了类型,并且在修改之后还有新的delta生成,那事情会非常麻烦。

修改字段后,新增delta是由insert引起的

比如红框部分
file

1 hdfs和txsql里都备份该表的数据
2 停止当前系统对该ORC事务表的任何操作,并将禁掉该表的comapction

alter table info disable compact;

按照类似普通ORC表操作方式,新建临时表,原始表每次保留字段类型相同的base或delta,分批导入临时表

CREATE TABLE info_t (id int ,name char(20) ,age int)
clustered BY (id) INTO 3 buckets stored AS orc_transaction;
hadoop dfs -mv /inceptor1/user/hive/warehouse/xiannv.db/hive/info/delta_0000029_0000029 /inceptor1/user/hive/warehouse/xiannv.db/hive/info_t

file

其他只剩下存储类型为id int,name string,age int的数据了。修改字段类型,从char(20)改为string

ALTER TABLE info CHANGE name name STRING;

将数据并入新表info_orc_t中

INSERT INTO info_t SELECT * FROM info;

file

如果修改字段后,新增delta是update/delete或merge into引入,目前没有好的办法恢复数据。

这篇文章对您有帮助吗?

平均评分 0 / 5. 次数: 0

尚无评价,您可以第一个评哦!

非常抱歉,这篇文章对您没有帮助.

烦请您告诉我们您的建议与意见,以便我们改进,谢谢您。