一、背景
- 由于公司很多表因为历史原因, 一开始都没有使用 ORC 格式,而是直接才用了 TextFile 格式, 随着业务的迭代, 表越变越大,导致存储资源十分紧张。为了节省存储空间,经过调研,我们决定采用 ORC 格式,大概可以节省 75% 的存储空间, 对于 PB 级别的数据, 是十分可观的。
- 在做完 ORC 格式后,我们又对这些表进行拉链操作。拉链的过程,遇到一个十分怪异的错误。不管使用 Hive 还是 Spark 都会抛出异常,导致 SQL 失败。
Hive 报错:
Spark 报的警告信息(对敏感字段作了打码):
1.1 问题表现:
- 该问题出错的表为分区表,当你
select * from xxxx where dt='yyyy-mm-dd'
查询最近一天的分区 能够成功,但是查询第一天或者不限制分区查询,却会抛出上述的错误。
二、问题定位
2.1 通过更加具体的报错提示,确定大体问题原因。
- 通过结合 Hive 的报错提示和 Spark 的警告信息,我们可以大概知道问题的原因是因为, 元数据记录的字段类型和真实的字段类型不一致,导致强制类型转换抛出异常。 但是具体的原因,依然我们进一步排查。
- 在 Hive 开发的 Jira 上可以找到以下几个 issue.
ORC Schema Evolution Issues
https://issues.apache.org/jira/browse/HIVE-10591
https://issues.apache.org/jira/browse/HIVE-11981
通过上述三个 issue 的描述,可以知道两个 ORC 相关的重要信息
- 从 Hive2.1 之前 是 不支持修改字段类型.
- 从 Hive2.1 开始 才 支持字段类型的更改。
- 从 Hive2.2 开始,才 支持 字段顺序的更改。
而我公司使用的 Hive 是 1.1.0,自然不能够支持 Hive 的字段类型修改。
所以问题大体原因: 应该是由于该表的字段类型做过变更, 由于该表又是 ORC 格式,导致无法兼容旧的分区,但是对于新的分区则按更改后的字段类型,生成对应格式的 ORC 文件。
2.2 通过 Git 查看了 SQL 代码的更改时间,进行印证。
- 既然找到了,大体问题,为了进一步印证, 我们过 Git 对比下更改前和更改后的版本。
- 最后发现更改后 sql,将原先的 Array 类型改成了 String 类型。
2.3 寻找开始有问题的分区。
- 通过 2.1 和 2.2 我们可以断定问题的原因。但是如何确定从哪一天的分区开始不兼容的?我们这个表有 400 多个分区,如何快速的进行查找?
- 由于表是分区表,那么这两部分不兼容的分区肯定是集中在一起,并且应为分区的是按时间递增,所以我们可以用二分查找来定位这个问题。
- 最后定位到了 2019-02-20 号这个分区是最后一个旧格式分区,21 号开始就是新格式分区。所以可以推断 2019-02-20 之前的分区都是旧格式分区。
那么我们要如何解决不兼容的分区?总不该把数据进行删除了,博主想了两种办法。 下面就以模拟的分区表来做验证。
三、 问题模拟
create database tmp;
create table tmp.test(
id string,
apps Array<string>
) partitioned by(dt string)
stored as orc;
insert overwrite table tmp.test partition(dt='2020-01-13')
select '1', array("a-app","b-app");
alter table tmp.test change apps apps array<array<string>>;
insert overwrite table tmp.test partition(dt='2020-01-14')
select '1', array(array("c-app","d-app")))
select * from tmp.test;
插入模拟数据
使用 Hive Alter
Alter 后插入新数据
指定分区查询
全表查询
可以很清晰的看到更改完格式后,旧的分区数据无法正常读取,但是读取新的分区格式却没有问题.
解决方案
这里提供两种解决方法的思路
- 方案一、通过外表挂载回写数据.
- 方案二、将 orc 文件拷贝到另外一个临时目录,通过内表挂载, 回写数据.
2.1 先建恢复表
create table tmp.test_4_recover(
id string,
apps Array<string>
) partitioned by(dt string)
stored as orc;
2.2 拷贝数据
执行 hadoop distcp 或者 hdfs dfs -cp 拷贝数据
hdfs dfs -cp /user/hive/warehouse/tmp.db/test/dt=2020-01-13 /user/hive/warehouse/tmp.db/test_4_recover/
2.3 挂载分区数据
MSCK REPAIR TABLE tmp.test_4_recover;
2.4 回写数据恢复并查看
insert overwrite table tmp.test partition(dt='2020-01-13')
select
id,
array(apps)
from
tmp.test_4_recover where dt='2020-01-13'
四、后续
- 如果采用了 ORC 格式,一定要谨慎的 ALTER 修改字段类型。因为这会导致 Hive 的元数据保存的是最新的字段类型信息,但是旧的 ORC 文件 底层存储结构并没有发生改变。一旦再去查这些旧的数据就会抛出异常。目前直到测试用的最新版本的 Hive 依然没有修复这个问题。
- 修复方案,推荐采用外表挂载的方式修复。有的公司会对外表进行限制,或者限制不能有两张表指向同一个 location, 那么这时候可以先将 旧的 orc 格式文件拷贝到另外一个 临时目录,再建一张临时表,将数据挂载到临时表,再用新的逻辑处理这些旧数据,重新插回新表中。
- 如果表是 orc 文件抛出类试 cast to 类型的错误.很可能是因为更改了字段类型导致.
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于