(数据开发篇)-001-ORC 文件谨慎 ALTER

本贴最后更新于 1801 天前,其中的信息可能已经物是人非

一、背景

  • 由于公司很多表因为历史原因, 一开始都没有使用 ORC 格式,而是直接才用了 TextFile 格式, 随着业务的迭代, 表越变越大,导致存储资源十分紧张。为了节省存储空间,经过调研,我们决定采用 ORC 格式,大概可以节省 75% 的存储空间, 对于 PB 级别的数据, 是十分可观的。
  • 在做完 ORC 格式后,我们又对这些表进行拉链操作。拉链的过程,遇到一个十分怪异的错误。不管使用 Hive 还是 Spark 都会抛出异常,导致 SQL 失败。

Hive 报错:
image.png

Spark 报的警告信息(对敏感字段作了打码):
image.png

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;

插入模拟数据
image.png

使用 Hive Alter
image.png

Alter 后插入新数据
image.png

指定分区查询
image.png

全表查询
image.png

可以很清晰的看到更改完格式后,旧的分区数据无法正常读取,但是读取新的分区格式却没有问题.

解决方案

这里提供两种解决方法的思路

  • 方案一、通过外表挂载回写数据.
  • 方案二、将 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;

image.png

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'

image.png

四、后续

  • 如果采用了 ORC 格式,一定要谨慎的 ALTER 修改字段类型。因为这会导致 Hive 的元数据保存的是最新的字段类型信息,但是旧的 ORC 文件 底层存储结构并没有发生改变。一旦再去查这些旧的数据就会抛出异常。目前直到测试用的最新版本的 Hive 依然没有修复这个问题。
  • 修复方案,推荐采用外表挂载的方式修复。有的公司会对外表进行限制,或者限制不能有两张表指向同一个 location, 那么这时候可以先将 旧的 orc 格式文件拷贝到另外一个 临时目录,再建一张临时表,将数据挂载到临时表,再用新的逻辑处理这些旧数据,重新插回新表中。
  • 如果表是 orc 文件抛出类试 cast to 类型的错误.很可能是因为更改了字段类型导致.

微信阅读体验更佳

simpledw.JPG

  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    343 引用 • 723 回帖
  • 一些有用的避坑指南。

    69 引用 • 93 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...