数据湖iceberg-day02-Hive与Iceberg整合

hive整合iceberg

1.6 Hive与Iceberg整合

Iceberg就是一种表格式,支持使用Hive对Iceberg进行读写操作,但是对Hive的版本有要求,如下:

操作 Hive 2.x Hive 3.1.2
CREATE EXTERNAL TABLE
CREATE TABLE
DROP TABLE
SELECT
INSERT INTO

这里基于Hive3.1.2版本进行Hive操作Iceberg表讲解。

1.6.1 开启Hive支持Iceberg

  1. 下载iceberg-hive-runtime.jar

想要使用Hive支持查询Iceberg表,首先需要下载“iceberg-hive-runtime.jar”,Hive通过该Jar可以加载Hive或者更新Iceberg表元数据信息。下载地址:https://iceberg.apache.org/#releases/:

数据湖iceberg-day02-Hive与Iceberg整合_第1张图片

将以上jar包下载后,上传到Hive服务端和客户端对应的lib目录下。另外在向Hive中Iceberg格式表插入数据时需要到“libfb303-0.9.3.jar”包,将此包也上传到Hive服务端和客户端对应的lib目录下。

数据湖iceberg-day02-Hive与Iceberg整合_第2张图片

数据湖iceberg-day02-Hive与Iceberg整合_第3张图片

  1. 配置hive-site.xml

在Hive客户端$HIVE_HOME/conf/hive-site.xml中添加如下配置:


    iceberg.engine.hive.enabled
    true

1.6.2 Hive中操作Iceberg格式表

从Hive引擎的角度来看,在运行环境中有Catalog概念(catalog主要描述了数据集的位置信息,就是元数据),Hive与Iceberg整合时,Iceberg支持多种不同的Catalog类型,例如:Hive、Hadoop、第三方厂商的AWS Glue和自定义Catalog。在实际应用场景中,Hive可能使用上述任意Catalog,甚至跨不同Catalog类型join数据,为此Hive提供了org.apache.iceberg.mr.hive.HiveIcebergStorageHandler(位于包iceberg-hive-runtime.jar)来支持读写Iceberg表,并通过在Hive中设置“iceberg.catalog..type”属性来决定加载Iceberg表的方式,该属性可以配置:hive、hadoop,其中“”是自己随便定义的名称,主要是在hive中创建Iceberg格式表时配置iceberg.catalog属性使用。

在Hive中创建Iceberg格式表时,根据创建Iceberg格式表时是否指定iceberg.catalog属性值,有以下三种方式决定Iceberg格式表如何加载(数据存储在什么位置)。

  1. 如果没有设置iceberg.catalog属性,默认使用HiveCatalog来加载

这种方式就是说如果在Hive中创建Iceberg格式表时,不指定iceberg.catalog属性,那么数据存储在对应的hive warehouse路径下。

在Hive客户端node3节点进入Hive,操作如下:

create table test_iceberg_1(
id int ,
name string,
age int
) 
partitioned by (dt string) 
stored by 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler';


#在Hive中加载如下两个包,在向Hive中插入数据时执行MR程序时需要使用到   /opt/cloudera/parcels/CDH/lib/hive/lib
#每次重新登录到客户端都需要重新加载这两个jar包
hive> add jar /opt/cloudera/parcels/CDH/lib/hive/lib/iceberg-hive-runtime-0.12.1.jar;
hive> add jar /opt/cloudera/parcels/CDH/lib/hive/lib/libfb303-0.9.3.jar;

#向表中插入数据
hive> insert into test_iceberg_1 values (1,"安徽",18,"20221108");
#查看是否能存储中文格式的数据
insert into test_iceberg_1 values (2,"百诚慧通",18,"20221108");  #查看结果后,表示可以存储中文格式并不会出现乱码情况

#查询表中的数据
select * from test_iceberg_1;

查看表:

数据湖iceberg-day02-Hive与Iceberg整合_第4张图片

在Hive默认的warehouse目录下可以看到创建的表目录:

数据湖iceberg-day02-Hive与Iceberg整合_第5张图片

点进去 test_iceberg_1:

data文件是数据文件

metadata是源文件:其中包含了有 JSON文件和AVRO后缀的文件。后面详细讲解、

temp是临时文件

数据湖iceberg-day02-Hive与Iceberg整合_第6张图片

  1. 如果设置了iceberg.catalog对应的catalog名字,就用对应类型的catalog加载

这种情况就是说在Hive中创建Iceberg格式表时,如果指定了iceberg.catalog属性值,那么数据存储在指定的catalog名称对应配置的目录下。

在Hive客户端node3节点进入Hive,操作如下:

#注册一个HiveCatalog叫another_hive
hive> set iceberg.catalog.another_hive.type=hive;

#在Hive中创建iceberg格式表
create table test_iceberg_2(
id int,
name string,
age int
)
partitioned by (dt string)
stored by 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
tblproperties ('iceberg.catalog'='another_hive');

# 注意如果重新启动了hive的客户端必须将 iceberg-hive-runtime-0.12.1.jar libfb303-0.9.3.jar两个jar包重新
#加载一下否则会报一下错误
=============================================== 错 误 ===============================================================================
22/11/09 09:21:57 ERROR metadata.Hive: Table test_iceberg_tbl2 not found: test_iceberg.test_iceberg_tbl2 table not found
FAILED: SemanticException org.apache.hadoop.hive.ql.metadata.InvalidTableException: Table not found test_iceberg_tbl2
22/11/09 09:21:57 ERROR ql.Driver: FAILED: SemanticException org.apache.hadoop.hive.ql.metadata.InvalidTableException: Table not found test_iceberg_tbl2
org.apache.hadoop.hive.ql.parse.SemanticException: org.apache.hadoop.hive.ql.metadata.InvalidTableException: Table not found test_iceberg_tbl2

#在Hive中加载如下两个包,在向Hive中插入数据时执行MR程序时需要使用到
hive> add jar /opt/cloudera/parcels/CDH/lib/hive/lib/iceberg-hive-runtime-0.12.1.jar;
hive> add jar /opt/cloudera/parcels/CDH/lib/hive/lib/libfb303-0.9.3.jar;

#插入数据,并查询
hive> insert into test_iceberg_tbl2 values (2,"wb",19,"20221109");
hive> select * from test_iceberg_tbl2;

以上方式指定“iceberg.catalog.another_hive .type=hive”后,实际上就是使用的hive的catalog,这种方式与第一种方式不设置效果一样,创建后的表存储在hive默认的warehouse目录下。也可以在建表时指定location 写上路径,将数据存储在自定义对应路径上。

数据湖iceberg-day02-Hive与Iceberg整合_第7张图片

除了可以将catalog类型指定成hive之外,还可以指定成hadoop,在Hive中创建对应的iceberg格式表时需要指定location来指定iceberg数据存储的具体位置,这个位置是具有一定格式规范 的自定义路径。在Hive客户端node3节点进入Hive,操作如下,没有测试

#注册一个HadoopCatalog叫hadoop
hive> set iceberg.catalog.hadoop.type=hadoop;

#使用HadoopCatalog时,必须设置“iceberg.catalog..warehouse”指定warehouse路径
hive> set iceberg.catalog.hadoop.warehouse=hdfs://mycluster/iceberg_data;

#在Hive中创建iceberg格式表,这里创建成外表
create external table test_iceberg_tbl3(
id int,
name string,
age int
)
partitioned by (dt string)
stored by 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
location 'hdfs://mycluster/iceberg_data/default/test_iceberg_tbl3'
tblproperties ('iceberg.catalog'='hadoop');

注意:以上location指定的路径必须是“iceberg.catalog.hadoop.warehouse”指定路径的子路径,格式必须是${iceberg.catalog.hadoop.warehouse}/${当前建表使用的hive库}/${创建的当前iceberg表名}

#在Hive中加载如下两个包,在向Hive中插入数据时执行MR程序时需要使用到
hive> add jar /software/hive-3.1.2/lib/iceberg-hive-runtime-0.12.1.jar;
hive> add jar /software/hive-3.1.2/lib/libfb303-0.9.3.jar;

#插入数据,并查询
hive> insert into test_iceberg_tbl3 values (3,"ww",20,"20211213");
hive> select * from test_iceberg_tbl3;
OK
3	ww	20	20211213

在指定的“iceberg.catalog.hadoop .warehouse”路径下可以看到创建的表目录:

数据湖iceberg-day02-Hive与Iceberg整合_第8张图片

  1. 如果iceberg.catalog属性设置为“location_based_table”,可以从指定的根路径下加载Iceberg 表 (结合spark和flink使用)

这种情况就是说如果HDFS中已经存在iceberg格式表,我们可以通过在Hive中创建Icerberg格式表指定对应的location路径映射数据。,在Hive客户端中操作如下:

CREATE TABLE test_iceberg_tbl4  (
  id int, 
  name string,
  age int
)
PARTITIONED BY (
   dt string
) STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler' 
LOCATION 'hdfs://mycluster/spark/person' 
TBLPROPERTIES ('iceberg.catalog'='location_based_table');

注意:指定的location路径下必须是iceberg格式表数据,并且需要有元数据目录才可以。不能将其他数据映射到Hive iceberg格式表。

注意:由于Hive建表语句分区语法“Partitioned by”的限制,如果使用Hive创建Iceberg格式表,目前只能按照Hive语法来写,底层转换成Iceberg标识分区,这种情况下不能使用Iceberge的分区转换,例如:days(timestamp),如果想要使用Iceberg格式表的分区转换标识分区,需要使用Spark或者Flink引擎创建表。

1.7 Iceberg表数据组织与查询

1.7.1 下载avro-tools jar包

由于后期需要查看avro文件内容,我们可以通过avro-tool.jar来查看avro数据内容。可以在以下网站中下载avro-tools对应的jar包,下载之后上传到node5节点上:

“https://mvnrepository.com/artifact/org.apache.avro/avro-tools”。

查看avro文件信息可以直接执行如下命令,可以将avro中的数据转换成对应的json数据。

java -jar /test/iceberg/avro-tools-1.8.1.jar tojson snap-5474573454114086650-1-516f560d-baaf-4a86-b2b5-282abb5e2f22.avro

1.7.2 在Hive中创建Iceberg表并插入数据

在Hive中创建Iceberg格式表,并插入如下数据:

#在Hive中创建iceberg格式表
create table test_iceberg_tb4(
id int ,
name string,
age int) 
partitioned by (dt string) 
stored by 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler';

# 测试修改表名
ALTER TABLE test_iceberg_tb4 RENAME  test_iceberg_4;  #测试不成功
#插入如下数据
insert into test_iceberg_tb4 values (1,"张三",21,"20211212");
insert into test_iceberg_tb4 values (2,"李四",22,"20211212");
insert into test_iceberg_tb4 values (3,"王五",23,"20211213");
insert into test_iceberg_tb4 values (4,"赵六",24,"20211213");
insert into test_iceberg_tb4 values (5,"孙七",25,"20211213");

1.7.3 查看Iceberg底层数据存储

下图为Iceberg表“test_iceberg_tbl1”在HDFS中存储的数据组织图:

数据湖iceberg-day02-Hive与Iceberg整合_第9张图片

通过上图我们可以看到有5个Snapshot快照,以上5个Snapshot实际上就是对应了5个Manifest list清单列表。

  1. 查询最新快照数据

为了了解Iceberg如何查询最新数据,可以参照下面这张图来详细了解底层实现。

数据湖iceberg-day02-Hive与Iceberg整合_第10张图片

查询Iceberg表数据时,首先获取最新的metadata信息,这里先获取到“00000-ec504.metadata.json”元数据信息,解析当前元数据文件可以拿到当前表的快照id:“949358624197301886”以及这张表的所有快照信息,也就是json信息中snapshots数组对应的值。根据当前表的快照id值可以获取对应的snapshot对应的avro文件信息:“snap--32800.avro”,我们可以找到当前快照对应的路径,看到其包含的Manifest 清单文件有5个:"32800-m0.avro"、"2abba-m0.avro"、"d33de-m0.avro"、"748bf-m0.avro"、"*b946e-m0.avro",读取该Iceberg格式表最新数据就是读取这几个文件中描述对应的parquet数据文件即可。

我们可以看到“snap-*-32800.avro”快照文件中不仅有包含的manifest路径信息,还有“added_data_files_count”、“existing_data_files_count”、“deleted_data_files_count”三个属性,Iceberg 根据 deleted_data_files_count 大于 0 来判断对应的manifest清单文件里面是不是被删除的数据,如果一个manifest清单文件该值大于0代表数据删除,读数据时就无需读这个manifest清单文件对应的数据文件。

根据Manifest list找到了各个对应的manifest 清单文件,每个文件中描述了对应parquet文件存储的位置信息,可以看到在对应的avro文件中有“status”属性,该属性为1代表对应的parquet文件为新增文件,需要读取,为2代表parquet文件被删除。

  1. 查询某个快照的数据

Apache Iceberg支持查询历史上任何时刻的快照,在查询时需要指定snapshot-id属性即可,这个只能通过Spark/Flink来查询实现,例如在Spark中查询某个快照数据如下:

spark.read.option("snapshot-id",6155408340798912701L).format("iceberg").load("path")

查询某个快照数据的原理如下图所示(以查询快照id为“6155408340798912701”的数据为例):

数据湖iceberg-day02-Hive与Iceberg整合_第11张图片

通过上图可以看出,实际上读取历史快照数据和读取最新数据不同之处就是找到的snapshot-id不同而已,原理都是一样。

  1. 根据时间戳查看某个快照的数据

Apache iceberg还支持通过as-of-timestamp参数执行时间戳来读取某个快照的数据,同样也是通过Spark/Flink来读取,Spark读取代码如下:

spark.read.option("as-of-timestamp","时间戳").format("iceberg").load("path")

实际上通过时间戳找到对应数据文件的原理与通过snapshot-id找到数据文件原理一样,在*.metadata.json文件中,除了有“current-snapshot-id”、“snapshots”属性外还有“snapshot-log”属性,该属性对应的值如下:

数据湖iceberg-day02-Hive与Iceberg整合_第12张图片

我们可以看到其中有个 timestamp-ms 属性和 snapshot-id 属性,并且是按照 timestamp-ms 升序的。在 Iceberg 内部实现中,它会将 as-of-timestamp 指定的时间和 snapshot-log 数组里面每个元素的 timestamp-ms 进行比较,找出最后一个满足 timestamp-ms <= as-of-timestamp 对应的 snapshot-id,原理同上,通过snapshot-id再找到要读取的数据文件。

1.7.4自己测试理解:

现在我们以插入最后一条数据为例

  1. 先将存储在hdfs上的test_iceberg_tb4文件全部拉下来(重点)

数据湖iceberg-day02-Hive与Iceberg整合_第13张图片

  1. 首先我们查看json格式的文件

    ====================================================第二次插入数据的json文件============================================================
    {
      "format-version" : 1,
      "table-uuid" : "132d9e89-ba03-4bec-9b5b-146896a5b595",
      "location" : "hdfs://nameservice1/user/hive/warehouse/test_iceberg.db/test_iceberg_tb4",
      "last-updated-ms" : 1667958364508,
      "last-column-id" : 4,
      "schema" : {
        "type" : "struct",
        "schema-id" : 0,
        "fields" : [ {
          "id" : 1,
          "name" : "id",
          "required" : false,
          "type" : "int"
        }, {
          "id" : 2,
          "name" : "name",
          "required" : false,
          "type" : "string"
        }, {
          "id" : 3,
          "name" : "age",
          "required" : false,
          "type" : "int"
        }, {
          "id" : 4,
          "name" : "dt",
          "required" : false,
          "type" : "string"
        } ]
      },
      "current-schema-id" : 0,
      "schemas" : [ {
        "type" : "struct",
        "schema-id" : 0,
        "fields" : [ {
          "id" : 1,
          "name" : "id",
          "required" : false,
          "type" : "int"
        }, {
          "id" : 2,
          "name" : "name",
          "required" : false,
          "type" : "string"
        }, {
          "id" : 3,
          "name" : "age",
          "required" : false,
          "type" : "int"
        }, {
          "id" : 4,
          "name" : "dt",
          "required" : false,
          "type" : "string"
        } ]
      } ],
      "partition-spec" : [ {
        "name" : "dt",
        "transform" : "identity",
        "source-id" : 4,
        "field-id" : 1000
      } ],
      "default-spec-id" : 0,
      "partition-specs" : [ {
        "spec-id" : 0,
        "fields" : [ {
          "name" : "dt",
          "transform" : "identity",
          "source-id" : 4,
          "field-id" : 1000
        } ]
      } ],
      "last-partition-id" : 1000,
      "default-sort-order-id" : 0,
      "sort-orders" : [ {
        "order-id" : 0,
        "fields" : [ ]
      } ],
      "properties" : {
        "engine.hive.enabled" : "true",
        "storage_handler" : "org.apache.iceberg.mr.hive.HiveIcebergStorageHandler"
      },
      "current-snapshot-id" : 7496993227593362873,             --通过current-snapshot-id找到第三次插入数据的快照
      "snapshots" : [ {
        "snapshot-id" : 5474573454114086650,
        "timestamp-ms" : 1667958335813,
        "summary" : {
          "operation" : "append",
          "added-data-files" : "1",
          "added-records" : "1",
          "added-files-size" : "1164",
          "changed-partition-count" : "1",
          "total-records" : "1",
          "total-files-size" : "1164",
          "total-data-files" : "1",
          "total-delete-files" : "0",
          "total-position-deletes" : "0",
          "total-equality-deletes" : "0"
        },
        "manifest-list" : "hdfs://nameservice1/user/hive/warehouse/test_iceberg.db/test_iceberg_tb4/metadata/snap-5474573454114086650-1-516f560d-baaf-4a86-b2b5-282abb5e2f22.avro",
        "schema-id" : 0
      }, {
        "snapshot-id" : 7496993227593362873,
        "parent-snapshot-id" : 5474573454114086650,
        "timestamp-ms" : 1667958364508,
        "summary" : {
          "operation" : "append",
          "added-data-files" : "1",
          "added-records" : "1",
          "added-files-size" : "1163",
          "changed-partition-count" : "1",
          "total-records" : "2",
          "total-files-size" : "2327",
          "total-data-files" : "2",
          "total-delete-files" : "0",
          "total-position-deletes" : "0",
          "total-equality-deletes" : "0"
        },
        "manifest-list" : "hdfs://nameservice1/user/hive/warehouse/test_iceberg.db/test_iceberg_tb4/metadata/snap-7496993227593362873-1-7bee7ff8-73cf-48ef-be4d-e6a912f5041b.avro",                             -- 当然这里也有第三次快照的坐标
        "schema-id" : 0
      } ],
      "snapshot-log" : [ {
        "timestamp-ms" : 1667958335813,
        "snapshot-id" : 5474573454114086650
      }, {
        "timestamp-ms" : 1667958364508,
        "snapshot-id" : 7496993227593362873
      } ],
      "metadata-log" : [ {												-- 下面这两个是上两次插入数据时的json格式时间戳和json格式。
        "timestamp-ms" : 1667958025965,
        "metadata-file" : "hdfs://nameservice1/user/hive/warehouse/test_iceberg.db/test_iceberg_tb4/metadata/00000-cd8309bc-1ff6-4668-940a-30d573ea1228.metadata.json"
      }, {
        "timestamp-ms" : 1667958335813,
        "metadata-file" : "hdfs://nameservice1/user/hive/warehouse/test_iceberg.db/test_iceberg_tb4/metadata/00001-50615358-d65e-4d86-a556-e99c20d6c216.metadata.json"
      } ]
    }
  2. 通过上面的json格式的内容可以找到对应的.avro格式的快照,然后解析

数据湖iceberg-day02-Hive与Iceberg整合_第14张图片

{
    "manifest_path": "hdfs://nameservice1/user/hive/warehouse/test_iceberg.db/test_iceberg_tb4/metadata/516f560d-baaf-4a86-b2b5-282abb5e2f22-m0.avro",
    "manifest_length": 6174,
    "partition_spec_id": 0,
    "added_snapshot_id": {
        "long": 5474573454114086650
    },
    "added_data_files_count": {
        "int": 1
    },
    "existing_data_files_count": {
        "int": 0
    },
    "deleted_data_files_count": {
        "int": 0
    },
    "partitions": {
        "array": [
            {
                "contains_null": false,
                "contains_nan": {
                    "boolean": false
                },
                "lower_bound": {
                    "bytes": "20211212"
                },
                "upper_bound": {
                    "bytes": "20211212"
                }
            }
        ]
    },
    "added_rows_count": {
        "long": 1
    },
    "existing_rows_count": {
        "long": 0
    },
    "deleted_rows_count": {
        "long": 0
    }
}

{
    "manifest_path": "hdfs://nameservice1/user/hive/warehouse/test_iceberg.db/test_iceberg_tb4/metadata/7bee7ff8-73cf-48ef-be4d-e6a912f5041b-m0.avro",
    "manifest_length": 6171,
    "partition_spec_id": 0,
    "added_snapshot_id": {
        "long": 7496993227593362873
    },
    "added_data_files_count": {
        "int": 1
    },
    "existing_data_files_count": {
        "int": 0
    },
    "deleted_data_files_count": {
        "int": 0
    },
    "partitions": {
        "array": [
            {
                "contains_null": false,
                "contains_nan": {
                    "boolean": false
                },
                "lower_bound": {
                    "bytes": "20211212"
                },
                "upper_bound": {
                    "bytes": "20211212"
                }
            }
        ]
    },
    "added_rows_count": {
        "long": 1
    },
    "existing_rows_count": {
        "long": 0
    },
    "deleted_rows_count": {
        "long": 0
    }
}

你可能感兴趣的:(hive,hadoop,大数据,数据仓库,hdfs)