作者:孟涛

背景

Hudi 源表对应一份 HDFS 数据,通过 Spark,Flink 组件或者 Hudi CLI,可以将 Hudi 表的数据映射为 Hive 外部表,基于该外部表, Hive可以方便的进行实时视图,读优化视图以及增量视图的查询。

Hive On Hudi 集成

这里以 hive3.1.1、hudi 0.9.0为例, 其他版本类似
1)将 hudi-hadoop-mr-bundle-0.9.0xxx.jar , hudi-hive-sync-bundle-0.9.0xx.jar 放到 hiveserver 节点的lib目录下;
2)修改 hive-site.xml 找到 hive.default.aux.jars.path 以及 hive.aux.jars.path 这两个配置项,将第一步中的jar包全路径给配置上去;

配置后大概长这个样子:

  1. <name>hive.default.aux.jars.path</name>
  2. <value>xxxx,jar,xxxx,jar,file:///mypath/hudi-hadoop-mr-bundle-0.9.0xxx.jar,file:///mypath/hudi-hive-sync-bundle-0.9.0xx.jar</value>

3) 配置完后请重启 hive-server;
4) 对于 Hudi 的 bootstrap 表(tez查询),除了要添加 hudi-hadoop-mr-bundle-0.9.0xxx.jar、hudi-hive-sync-bundle-0.9.0xx.jar 这两个jar包, 还需把 hbase-shaded-miscellaneous-xxx.jar、hbase-metric-api-xxx.jar、hbase-metrics-xxx.jar、hbase-protocol-shaded-xx.jar、hbase-shaded-protobuf-xxx.jar、htrce-core4-4.2.0xxxx.jar 按上述步骤添加进去。

创建 Hive 外表

一般来说 Hudi 表在用 Spark 或者 Flink 写入数据时会自动同步到 Hive 外部表, 此时可以直接通过 beeline 查询同步的外部表, 若写入引擎没有开启自动同步,则需要手动利用 hudi 客户端工具 run_hive_sync_tool.sh 进行同步具体可以参考官网查看相关参数。

查询 Hive 外表

操作前提

使用 Hive 查询 Hudi 表前,需要通过set命令设置 hive.input.format,否则会出现数据重复,查询异常等错误,如下面这个报错就是典型的没有设置 hive.input.format 导致的:

java.lang.IllegalArgumentException: HoodieRealtimeReader can oly work on RealTimeSplit and not with xxxxxxxxxx

除此之外对于增量查询,还需要 set 命令额外设置3个参数,

set hoodie.mytableName.consume.mode=INCREMENTAL;
set hoodie.mytableName.consume.max.commits=3;
set hoodie.mytableName.consume.start.timestamp=commitTime;

注意这3个参数是表级别参数

参数名 描述
hoodie.mytableName.consume.mode Hudi表的查询模式。
增量查询 :INCREMENTAL
非增量查询:不设置或者设为SNAPSHOT
hoodie.mytableName.consume.start.timestamp Hudi表增量查询起始时间。
hoodie. mytableName.consume.max.commits Hudi表基于 hoodie.mytableName.consume.start.timestamp
之后要查询的增量commit次数。
例如:
设置为3时,增量查询从指定的起始时间之后commit 3次的数据
设为-1时,增量查询从指定的起始时间之后提交的所有数据

COW 表查询

这里假设同步的 Hive 外表名为 hudi_cow。

实时视图

设置 hive.input.format 为 org.apache.hadoop.hive.ql.io.HiveInputFormat 或者org.apache.hudi.hadoop.hive.HoodieCombineHiveInputFormat,像普通的hive表一样查询即可:

set hive.input.format= org.apache.hadoop.hive.ql.io.HiveInputFormat;
select count(*) from hudi_cow;

增量视图

除了要设置 hive.input.format,还需要设置上述的3个增量查询参数,且增量查询语句中的必须添加 where 关键字并将 `_hoodie_commit_time > 'startCommitTime' 作为过滤条件(这地方主要是hudi的小文件合并会把新旧commit的数据合并成新数据,hive是没法直接从parquet文件知道哪些是新数据哪些是老数据)

set hive.input.format= org.apache.hadoop.hive.ql.io.HiveInputFormat;
set hoodie.hudicow.consume.mode= INCREMENTAL;
set hoodie.hudicow.consume.max.commits=3;
set hoodie.hudicow.consume.start.timestamp= xxxx;
select count(*) from hudicow where `_hoodie_commit_time`>'xxxx'
-- (这里注意`_hoodie_commit_time` 的引号是反引号(tab键上面那个)不是单引号, 'xxxx'是单引号)

MOR 表查询

这里假设 MOR 类型 Hudi 源表的表名为hudi_mor,映射为两张 Hive 外部表hudi_mor_ro(ro表)和 hudi_mor_rt(rt表)。

实时视图

设置了 hive.input.format 之后,即可查询到Hudi源表的最新数据

set hive.input.format= org.apache.hadoop.hive.ql.io.HiveInputFormat;
select * from hudicow_rt;

读优化视图

ro 表全称 read oprimized table,对于 MOR 表同步的 xxx_ro 表,只暴露压缩后的 parquet。其查询方式和COW表类似。设置完 hiveInputFormat 之后 和普通的 Hive 表一样查询即可。

增量视图

这个增量查询针对的rt表,不是ro表。同 COW 表的增量查询类似:

set hive.input.format=org.apache.hudi.hadoop.hive.HoodieCombineHiveInputFormat; // 这地方指定为HoodieCombineHiveInputFormat
set hoodie.hudimor.consume.mode=INCREMENTAL;
set hoodie.hudimor.consume.max.commits=-1;
set hoodie.hudimor.consume.start.timestamp=xxxx;
select * from hudimor_rt where `_hoodie_commit_time`>'xxxx';// 这个表名要是rt表

说明:

  • set hive.input.format=org.apache.hudi.hadoop.hive.HoodieCombineHiveInputFormat;最好只用于 rt 表的增量查询 当然其他种类的查询也可以设置为这个,这个参数会影响到普通的hive表查询,因此在rt表增量查询完成后,应该设置 set hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat; 或者改为默认值set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; 用于其他表的查询。
  • set hoodie.mytableName.consume.mode=INCREMENTAL; 仅用于该表的增量查询模式,若要对该表切换为其他查询模式,应设置set hoodie.hudisourcetablename.consume.mode=SNAPSHOT;

当前的一些问题

  • Hive读 Hudi 表会将所有的数据给打印出来,这个有严重的性能问题和数据安全问题,请拉去社区的最新分支。
  • MOR 表增量视图的读取当前还是有不少问题的请拉取 https://github.com/apache/hudi/pull/3203 这个pr
  • MOR 表的实时视图读取,请按需设置 mapreduce.input.fileinputformat.split.maxsize 的大小 禁止hive取切分读取的文件,否则会出现数据重复。这个问题当前是无解 的,Spark 读 Hudi实时视图的时候代码直接写死不会切分文件,Hive需要手动设置。
  • 如果碰到 ClassNotFound, NoSuchMethod 等错误请检查 Hive lib库下面的jar包是否出现冲突。

Hive 3.1.0 兼容

Hive 3.1.0 的同学需要稍微改下 Hive 侧源码

  1. 修改 org.apache.hadoop.hive.common.FileUtils 函数:

    public static final PathFilter HIDDEN_FILES_PATH_FILTER = new PathFilter() {
    @Override
    public boolean accept(Path p) {
     String name = p.getName();
     boolean isHudiMeta = name.startsWith(".hoodie");
     boolean isHudiLog = false;
     Pattern LOG_FILE_PATTERN = Pattern.compile("\\.(.*)_(.*)\\.(.*)\\.([0-9]*)(_(([0-9]*)-([0-9]*)-([0-9]*)))?");
     Matcher matcher = LOG_FILE_PATTERN.matcher(name);
     if (matcher.find()) {
       isHudiLog = true;
     }
     boolean isHudiFile = isHudiLog || isHudiMeta;
     return (!name.startsWith("_") && !name.startsWith(".")) || isHudiFile;
    }
    };
    
  2. 重新编译 hive, 把新编译的 hive-common-xxx.jar, hive-exec-xxx.jar 替换到 hive server 的 lib 目录下 注意权限和名字和原来的jar包保持一致,重启hive-server。

    错误收集和反馈

    Hive 错误日志这块简单的提一下,请去 yarn 上取 mr 的详细错误日志,不要把 beeline 上的错误直接贴群里,那个意义不大。 最后大家碰到上问题可以在群里直接反馈,如有复现步骤 请附带进去。

如果是明确的 bug 可以到 HUDI-2649 下提 sub-task。