Hive+Spark+Flink集成Iceberg功能POC

393 阅读15分钟

hive集成iceberg

一、hive配置iceberg

  • 1、#增加依赖jar
mkdir auxlib
cp iceberg-hive-runtime-1.2.1.jar /opt/module/hive/auxlib
cp libfb303-0.9.3.jar /opt/module/hive/auxlib
  • 2、修改hive-site.xml配置(新增配置)
<property>
    <name>iceberg.engine.hive.enabled</name>
    <value>true</value>
</property>

<property>
    <name>hive.aux.jars.path</name>
    <value>/opt/module/hive/auxlib</value>
</property>

二、创建使用Catalog

配置项说明
iceberg.catalog..typeCatalog的类型: hive, hadoop, 如果使用自定义Catalog,则不设置
iceberg.catalog..catalog-implCatalog的实现类, 如果上面的type没有设置,则此参数必须设置
iceberg.catalog..Catalog的其他配置项

1、默认使用HiveCatalog

默认在hive配置参数中配置的warehouse下创建iceberg表dh1:

CREATE TABLE dh1 (i int) 
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler';

INSERT INTO dh1 values(1);

2、指定Catalog类型

1)使用HiveCatalog

set iceberg.catalog.iceberg_hive.type=hive;
set iceberg.catalog.iceberg_hive.uri=thrift://node86:9083;
set iceberg.catalog.iceberg_hive.clients=10;
set iceberg.catalog.iceberg_hive.warehouse=hdfs://node49:9000/warehouse/iceberg-hive;

CREATE TABLE dh2 (i int) 
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
TBLPROPERTIES('iceberg.catalog'='iceberg_hive');
 
INSERT INTO dh2 values(1);

2)使用HadoopCatalog

set iceberg.catalog.iceberg_hadoop.type=hadoop;
set iceberg.catalog.iceberg_hadoop.warehouse=hdfs://node49:9000/warehouse/iceberg-hadoop;

CREATE TABLE dh3 (i int) 
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler' 
LOCATION 'hdfs://node49:9000/warehouse/iceberg-hadoop/default/dh3'
TBLPROPERTIES('iceberg.catalog'='iceberg_hadoop');

 
INSERT INTO dh3 values(1);

3、指定路径加载

CREATE EXTERNAL TABLE dh4 (i int)
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
LOCATION 'hdfs://node49:9000/warehouse/iceberg-hadoop/default/dh3'
TBLPROPERTIES ('iceberg.catalog'='location_based_table');

三、基本操作

1、创建表

​ Hive语法创建分区表,不会在HMS中创建分区,而是将分区数据转换为Iceberg标识分区。这种情况下不能使用Iceberg的分区转换,例如:days(timestamp),如果想要使用Iceberg格式表的分区转换标识分区,需要使用Spark或者Flink引擎创建表 。

-- 1)创建外部表
CREATE EXTERNAL TABLE iceberg_create1 (i int) 
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler';

describe formatted iceberg_create1;
-- 2)创建内部表
CREATE TABLE iceberg_create2 (i int) 
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler';

describe formatted iceberg_create2;
-- 3)创建分区表
CREATE EXTERNAL TABLE iceberg_create3 (id int,name string)
PARTITIONED BY (age int)
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler';

describe formatted iceberg_create3;

2、修改表

​ 只支持HiveCatalog表修改表属性,Iceberg表属性和Hive表属性存储在HMS中是同步的 。

ALTER TABLE iceberg_create1 SET TBLPROPERTIES('external.table.purge'='FALSE');

3、插入表

支持标准单表INSERT INTO操作;在HIVE 3.x中,INSERT OVERWRITE虽然能执行,但其实是追加。

INSERT INTO iceberg_create2 VALUES (1);
INSERT INTO iceberg_create1 select * from iceberg_create2;

4、删除表

DROP TABLE iceberg_create1;

spark集成iceberg

一、spark3.3.1包上传到/opt/目录下解压

su root
cp spark331.tar.gz /opt/
tar -zxvf ./spark331.tar.gz ./
chown -R work:work ./spark331

二、Iceberg依赖jar等配置

  • 0、iceberg依赖上传
cd /opt/spark331
mkdir lib
cp iceberg-core-1.2.1.jar /opt/spark331/lib/
cp iceberg-hive-runtime-1.2.1.jar /opt/spark331/lib/
cp iceberg-spark-runtime-3.3_2.12-1.2.1.jar /opt/spark331/lib/
  • 1、spark-env.sh

  • 2、spark-default.conf

    /opt/spark331/conf/spark-default.conf

    最后几行设定了集群内的hiveCatalog和hadoopCatalog;具体设置也可以在spark-sql客户端内设定。

3、将hadoop现有配置文件copy到/opt/spark331/hadoopconf目录下

root@node86:/opt/spark331/hadoopconf# ll
总用量 52
drwxr-xr-x  2 work work  4096 5月  29 15:30 ./
drwxrwxr-x 15 work work  4096 5月  29 15:21 ../
-rwxr-xr-x  1 work work    32 5月  29 15:21 client-viewfs.xml*
-rwxr-xr-x  1 work work  2494 5月  29 15:21 core-site.xml*
-rwxr-xr-x  1 work work  6144 5月  29 15:21 hdfs-site.xml*
-rwxr-xr-x  1 work work  1665 5月  29 15:21 hive-exec-log4j.properties*
-rwxr-xr-x  1 work work  2033 5月  29 15:21 hive-log4j.properties*
-rwxr-xr-x  1 work work  3548 5月  29 15:30 hive-site.xml*
-rwxr-xr-x  1 work work  1585 5月  29 15:21 mapred-site.xml*
-rwxr-xr-x  1 work work 11958 5月  29 15:21 yarn-site.xml*

三、配置catalog

  • 1、hive Catalog配置
# vim conf/spark-defaults.conf
spark.sql.catalog.hive_prod = org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.hive_prod.type = hive
spark.sql.catalog.hive_prod.uri = thrift://nodeIP:9083
  • 2、hadoop Catalog配置
# vim conf/spark-defaults.conf
spark.sql.catalog.hadoop_prod = org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.hadoop_prod.type = hadoop
spark.sql.catalog.hadoop_prod.warehouse = hdfs://hadoop-test01:9000/warehouse/hadoop_prod

四、spark-sql操作

1、进入客户端

su work 
# 进入客户端
/opt/spark331/bin/spark-sql

2、常规操作

1)创建表

-- PARTITIONED BY (partition-expressions) :配置分区
-- LOCATION '(fully-qualified-uri)' :指定表路径
-- COMMENT 'table documentation' :配置表备注
-- TBLPROPERTIES ('key'='value', ...) :配置表属性
-- 表属性:https://iceberg.apache.org/docs/latest/configuration/
-- 对Iceberg表的每次更改都会生成一个新的元数据文件(json文件)以提供原子性。默认情况下,旧元数据文件作为历史文件保存不会删除。
-- 如果要自动清除元数据文件,在表属性中设置write.metadata.delete-after-commit.enabled=true。这将保留一些元数据文件(直到write.metadata.previous-versions-max),并在每个新创建的元数据文件之后删除旧的元数据文件。
use hadoop_prod;
create database default;
use default;
--基础表
CREATE TABLE hadoop_prod.default.sample1 (
    id bigint COMMENT 'unique id',
    data string)
USING iceberg;
--分区表
CREATE TABLE hadoop_prod.default.sample2 (
    id bigint,
    data string,
    category string)
USING iceberg
PARTITIONED BY (category);
--隐藏分区表
CREATE TABLE hadoop_prod.default.sample3 (
    id bigint,
    data string,
    category string,
    ts timestamp)
USING iceberg
PARTITIONED BY (bucket(16, id), days(ts), category);
-- 支持的转换有:
-- years(ts):按年划分
-- months(ts):按月划分
-- days(ts)或date(ts):等效于dateint分区
-- hours(ts)或date_hour(ts):等效于dateint和hour分区
-- bucket(N, col):按哈希值划分mod N个桶
-- truncate(L, col):按截断为L的值划分
-- 字符串被截断为给定的长度
-- 整型和长型截断为bin: truncate(10, i)生成分区0,10,20,30,…

2)删除表

-- 对于HadoopCatalog而言,运行DROP TABLE将从catalog中删除表并删除表内容。
CREATE EXTERNAL TABLE hadoop_prod.default.sample7 (
    id bigint COMMENT 'unique id',
    data string)
USING iceberg;
INSERT INTO hadoop_prod.default.sample7 values(1,'a');
DROP TABLE hadoop_prod.default.sample7;

--对于HiveCatalog而言:
-- 在0.14之前,运行DROP TABLE将从catalog中删除表并删除表内容。
-- 从0.14开始,DROP TABLE只会从catalog中删除表,不会删除数据。为了删除表内容,应该使用DROP table PURGE。
CREATE EXTERNAL TABLE hive_prod.default.sample7 (
    id bigint COMMENT 'unique id',
    data string)
USING iceberg;
INSERT INTO hive_prod.default.sample7 values(1,'a');
-- 删除表
DROP TABLE hive_prod.default.sample7;
-- 删除表和数据(谨慎使用,有问题)
DROP TABLE hive_prod.default.sample7 PURGE;

3)修改表

Iceberg在Spark 3中完全支持ALTER TABLE,包括:

Ø 重命名表

Ø 设置或删除表属性

Ø 添加、删除和重命名列

Ø 添加、删除和重命名嵌套字段

Ø 重新排序顶级列和嵌套结构字段

Ø 扩大int、float和decimal字段的类型

Ø 将必选列变为可选列

此外,还可以使用SQL扩展来添加对分区演变的支持和设置表的写顺序。

-- 测试表
CREATE TABLE hive_prod.default.sample1 (
    id bigint COMMENT 'unique id',
    data string)
USING iceberg;
-- 查看表结构
describe formatted hive_prod.default.sample1;
Ⅰ、修改表名
ALTER TABLE hive_prod.default.sample1 RENAME TO hive_prod.default.sample2;
Ⅱ、修改表属性
-- (1)修改表属性
ALTER TABLE hive_prod.default.sample1 SET TBLPROPERTIES (
    'read.split.target-size'='268435456'
);

ALTER TABLE hive_prod.default.sample1 SET TBLPROPERTIES (
    'comment' = 'A table comment.'
);
-- (2)删除表属性
ALTER TABLE hive_prod.default.sample1 UNSET TBLPROPERTIES ('read.split.target-size');

Ⅲ 、增加列
ALTER TABLE hive_prod.default.sample1
ADD COLUMNS (
    category string comment 'new_column'
  );

-- 添加struct类型的列
ALTER TABLE hive_prod.default.sample1
ADD COLUMN point struct<x: double, y: double>;

-- 往struct类型的列中添加字段
ALTER TABLE hive_prod.default.sample1
ADD COLUMN point.z double;

-- 创建struct的嵌套数组列
ALTER TABLE hive_prod.default.sample1
ADD COLUMN points array<struct<x: double, y: double>>;

-- 在数组中的结构中添加一个字段。使用关键字'element'访问数组的元素列。
ALTER TABLE hive_prod.default.sample1
ADD COLUMN points.element.z double;

-- 创建一个包含Map类型的列,key和value都为struct类型
ALTER TABLE hive_prod.default.sample1
ADD COLUMN pointsm map<struct<x: int>, struct<a: int>>;

-- 在Map类型的value的struct中添加一个字段。
ALTER TABLE hive_prod.default.sample1
ADD COLUMN pointsm.value.b int;
-- 在Spark 2.4.4及以后版本中,可以通过添加FIRST或AFTER子句在任何位置添加列:(只能使用在HadoopCatalog下)
ALTER TABLE hadoop_prod.default.sample1
ADD COLUMN new_column1 string AFTER data;
ALTER TABLE hadoop_prod.default.sample1
ADD COLUMN new_column2 string FIRST;
Ⅳ、修改列
--(1)修改列名
ALTER TABLE hadoop_prod.default.sample1 RENAME COLUMN data TO data1;
--(2)Alter Column修改类型(只允许安全的转换)
ALTER TABLE hadoop_prod.default.sample1
ADD COLUMNS (
    idd int
  );
ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN idd TYPE bigint;
--(3)Alter Column 修改列的注释
ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN id COMMENT 'b';
--(4)Alter Column修改列的顺序
ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN id FIRST;
ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN new_column2 AFTER new_column1;
--(5)Alter Column修改列是否允许为null
ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN id DROP NOT NULL;
-- ALTER COLUMN不用于更新struct类型。使用ADD COLUMN和DROP COLUMN添加或删除struct类型的字段。
Ⅴ、删除列
ALTER TABLE hadoop_prod.default.sample1 DROP COLUMN idd;
ALTER TABLE hive_prod.default.sample1 DROP COLUMN point.z;
Ⅵ、添加分区
ALTER TABLE hive_prod.default.sample1 ADD PARTITION FIELD category ;

ALTER TABLE hive_prod.default.sample1 ADD PARTITION FIELD bucket(16, id);
ALTER TABLE hive_prod.default.sample1 ADD PARTITION FIELD truncate(data, 4);
ALTER TABLE hive_prod.default.sample1 ADD PARTITION FIELD years(ts);

ALTER TABLE hive_prod.default.sample1 ADD PARTITION FIELD bucket(16, id) AS shard;
Ⅶ、删除分区
  • 注意,尽管删除了分区,但列仍然存在于表结构中。

  • 删除分区字段是元数据操作,不会改变任何现有的表数据。新数据将被写入新的分区,但现有数据将保留在旧的分区布局中。

  • 当分区发生变化时,动态分区覆盖行为也会发生变化。例如,如果按天划分分区,而改为按小时划分分区,那么覆盖将覆盖每小时划分的分区,而不再覆盖按天划分的分区。

  • 删除分区字段时要小心,可能导致元数据查询失败或产生不同的结果。

ALTER TABLE hadoop_prod.default.sample1 DROP PARTITION FIELD category;
ALTER TABLE hadoop_prod.default.sample1 DROP PARTITION FIELD bucket(16, id);
ALTER TABLE hadoop_prod.default.sample1 DROP PARTITION FIELD truncate(data, 4);
ALTER TABLE hadoop_prod.default.sample1 DROP PARTITION FIELD years(ts);
ALTER TABLE hadoop_prod.default.sample1 DROP PARTITION FIELD shard;
Ⅷ、修改分区
ALTER TABLE hive_prod.default.sample1 REPLACE PARTITION FIELD bucket(16, id) WITH bucket(8, id);
Ⅸ、修改表写入顺序
ALTER TABLE hive_prod.default.sample1 WRITE ORDERED BY category, id;

ALTER TABLE hive_prod.default.sample1 WRITE ORDERED BY category ASC, id DESC;

ALTER TABLE hive_prod.default.sample1 WRITE ORDERED BY category ASC NULLS LAST, id DESC NULLS FIRST;
-- 表写顺序不能保证查询的数据顺序。它只影响数据写入表的方式。
-- WRITE ORDERED BY设置了一个全局排序,即跨任务的行排序,就像在INSERT命令中使用ORDER BY一样:
INSERT INTO hive_prod.default.sample1
SELECT id, data, category, ts FROM another_table
ORDER BY ts, category;
-- 要在每个任务内排序,而不是跨任务排序,使用local ORDERED BY:
ALTER TABLE hive_prod.default.sample1 WRITE LOCALLY ORDERED BY category, id;
Ⅹ、按分区并行写入
ALTER TABLE hive_prod.default.sample1 WRITE DISTRIBUTED BY PARTITION LOCALLY ORDERED BY category, id;

4)插入数据

CREATE TABLE hadoop_prod.default.a (
    id bigint,
    count bigint)
USING iceberg;

CREATE TABLE hadoop_prod.default.b (
    id bigint,
count bigint,
flag string)
USING iceberg;

-- 1)Insert Into
INSERT INTO hadoop_prod.default.a VALUES (1, 1), (2, 2), (3, 3);
INSERT INTO hadoop_prod.default.b VALUES (1, 1, 'a'), (2, 2, 'b'), (4, 4, 'd');
-- 2)MERGE INTO行级更新
MERGE INTO hadoop_prod.default.a t 
USING (SELECT * FROM hadoop_prod.default.b) u ON t.id = u.id
WHEN MATCHED AND u.flag='b' THEN UPDATE SET t.count = t.count + u.count
WHEN MATCHED AND u.flag='a' THEN DELETE
WHEN NOT MATCHED THEN INSERT (id,count) values (u.id,u.count);

5)查询数据

-- 0)创建表并插入数据
CREATE EXTERNAL TABLE hadoop_prod.default.sample8 (
    id bigint COMMENT 'unique id',
    data string)
USING iceberg;
INSERT INTO hadoop_prod.default.sample8 values(1,'a');
INSERT INTO hadoop_prod.default.sample8 values(2,'c');
INSERT INTO hadoop_prod.default.sample8 values(3,'b');

-- 1)普通查询
SELECT count(1) as count, data
FROM hadoop_prod.default.sample8
GROUP BY data;
-- 2)查询元数据
-- // 查询表快照
SELECT * FROM hadoop_prod.default.sample8.snapshots;

-- // 查询数据文件信息
SELECT * FROM hadoop_prod.default.sample8.files;

-- // 查询表历史
SELECT * FROM hadoop_prod.default.sample8.history;

-- // 查询 manifest
SELECT * FROM hadoop_prod.default.sample8.manifests;

6)存储过程

Procedures可以通过CALL从任何已配置的Iceberg Catalog中使用。所有Procedures都在namespace中。

Ⅰ、语法
-- 按照参数名传参
CALL catalog_name.system.procedure_name(arg_name_2 => arg_2, arg_name_1 => arg_1)
-- 当按位置传递参数时,如果结束参数是可选的,则只有结束参数可以省略。
CALL catalog_name.system.procedure_name(arg_1, arg_2, ... arg_n)
Ⅱ、快照管理
-- // 查询表快照
SELECT * FROM hadoop_prod.default.sample8.snapshots;
-- (1)回滚到指定的快照id
CALL hadoop_prod.system.rollback_to_snapshot('default.sample8', 3394509036086431205);
-- (2)回滚到指定时间的快照
CALL hadoop_prod.system.rollback_to_timestamp('default.sample8', TIMESTAMP '2023-05-29 19:58:00.008');
-- (3)设置表的当前快照ID
CALL hadoop_prod.system.set_current_snapshot('default.sample8', 6476372975407164130);
-- (4)从快照变为当前表状态
CALL hadoop_prod.system.cherrypick_snapshot('default.sample8', 6476372975407164130);
-- 这个有问题,不要用这个写法切换快照
CALL hadoop_prod.system.cherrypick_snapshot(snapshot_id => 3394509036086431205, table => 'default.sample8' );
Ⅲ、元数据管理
-- // 查询表快照
SELECT * FROM hadoop_prod.default.sample8.snapshots;

--(1)删除早于指定日期和时间的快照,但保留最近100个快照:
CALL hadoop_prod.system.expire_snapshots('default.sample8', TIMESTAMP '2023-05-29 19:57:59.598', 100);
--(2)删除Iceberg表中任何元数据文件中没有引用的文件
--#列出所有需要删除的候选文件
CALL hadoop_prod.system.remove_orphan_files(table => 'default.sample8', dry_run => true);

--#删除指定目录中db.sample表不知道的任何文件
CALL hadoop_prod.system.remove_orphan_files(table => 'default.sample8', location => 'tablelocation/data');
--(3)合并数据文件(合并小文件)
CALL hadoop_prod.system.rewrite_data_files('default.sample8');

CALL hadoop_prod.system.rewrite_data_files(table => 'default.sample8', strategy => 'sort', sort_order => 'id DESC NULLS LAST,name ASC NULLS FIRST');

CALL hadoop_prod.system.rewrite_data_files(table => 'default.sample8', strategy => 'sort', sort_order => 'zorder(c1,c2)');

CALL hadoop_prod.system.rewrite_data_files(table => 'default.sample8', options => map('min-input-files','2'));

CALL hadoop_prod.system.rewrite_data_files(table => 'default.sample8', where => 'id = 3 and name = "foo"');
--(4)重写表清单来优化执行计划
CALL hadoop_prod.system.rewrite_manifests('default.sample8');

--#重写表db中的清单。并禁用Spark缓存的使用。这样做可以避免执行程序上的内存问题。
CALL hadoop_prod.system.rewrite_manifests('db.sample', false);
Ⅳ、迁移表
-- // 查询表快照
SELECT * FROM hadoop_prod.default.sample8.snapshots;

--(1)快照
CALL hadoop_prod.system.snapshot('default.sample8','default.sample8_1');
CALL hadoop_prod.system.snapshot('default.sample8', 'default.sample9', '/tmp/temptable/');
--(2)迁移
CALL hadoop_prod.system.migrate('hive_prod.default.sample8', map('foo', 'bar'))
CALL hadoop_prod.system.migrate('default.sample8')
--(3)添加数据文件
CALL hive_prod.system.add_files(
table => 'default.sample9',
source_table => 'default.sample8'
);

CALL hadoop_prod.system.add_files(
  table => 'db.tbl',
  source_table => '`parquet`.`path/to/table`'
)
Ⅴ、元数据信息
--(1)获取指定快照的父快照id
CALL hadoop_prodhadoop_prodhadoop_prod.system.ancestors_of('default.sample8')
--(2)获取指定快照的所有祖先快照
CALL hadoop_prod.system.ancestors_of('default.sample8', 3394509036086431205);
CALL hadoop_prod.system.ancestors_of(snapshot_id => 3394509036086431205, table => 'default.sample8')

四、启动spark-shell

1、进入客户端

su work 
# 进入客户端
/opt/spark331/bin/spark-shell

2、常规操作

1)自定义配置catalog(不设置默认取配置文件spark-default.conf中的)

var spark: SparkSession = SparkSession.builder().master("local").appName(this.getClass.getSimpleName)
  //指定hive catalog, catalog名称为iceberg_hive
  .config("spark.sql.catalog.iceberg_hive", "org.apache.iceberg.spark.SparkCatalog")
  .config("spark.sql.catalog.iceberg_hive.type", "hive")
  .config("spark.sql.catalog.iceberg_hive.uri", "thrift://node86:9083")
  //    .config("iceberg.engine.hive.enabled", "true")
  //指定hadoop catalog,catalog名称为iceberg_hadoop 
  .config("spark.sql.catalog.iceberg_hadoop", "org.apache.iceberg.spark.SparkCatalog")
  .config("spark.sql.catalog.iceberg_hadoop.type", "hadoop")
  .config("spark.sql.catalog.iceberg_hadoop.warehouse", "hdfs://node49:9000/warehouse/hadoop_prod")
  .getOrCreate()

2)读取表

Ⅰ、加载表
spark.read
.format("iceberg")
.load("hdfs://node49:9000/warehouse/hadoop_prod/default/a")
.show()
或
// 仅支持Spark3.0以上
spark.table("hadoop_prod.default.a").show()
Ⅱ、时间旅行:指定时间查询
spark.read.format("iceberg").load("hdfs://node49:9000/iceberg/default/a").show()

spark.read.option("as-of-timestamp", "499162860000").format("iceberg").load("hdfs://node49:9000/iceberg/default/a").show()
Ⅲ、时间旅行:指定快照id查询
spark.read.option("snapshot-id", 965039321871259587L).format("iceberg").load("hdfs://node49:9000/iceberg/default/a").show()
Ⅳ、增量查询
//左开右闭
spark.read.format("iceberg").option("start-snapshot-id", "5142428589913493912").option("end-snapshot-id", "190973832236374164").load("hdfs://node49:9000/iceberg/default/sample7").show()
//查询的表只能是append的方式写数据,不支持replace, overwrite, delete操作。

3)检查表

//1)查询元数据
spark.read.format("iceberg").load("hadoop_prod.default.sample7.files").show

spark.read.format("iceberg").load("hdfs://node49:9000/iceberg/default/sample7#files").show
//2)元数据表时间旅行查询
spark.read.format("iceberg").option("snapshot-id", 190973832236374164L).load("hadoop_prod.default.sample7.files").show

4)写入表

//1)创建样例类,准备DF
case class Sample(id:Int,data:String,category:String)

var df = spark.createDataFrame(Seq(Sample(1,"A","a"),Sample(2,"B", "b"),Sample(3,"C","c")))
//2)插入数据并建表
df.writeTo("hadoop_prod.default.table1").create()

import spark.implicits._
df.writeTo("hadoop_prod.default.table1").tableProperty("write.format.default", "orc").partitionedBy($"category").createOrReplace()
//3)append追加
df.writeTo("hadoop_prod.default.table1").append()
//4)动态分区覆盖
df.writeTo("hadoop_prod.default.table1").overwritePartitions()
//5)静态分区覆盖
import spark.implicits._
df.writeTo("hadoop_prod.default.table1").overwrite($"category" === "c")
//6)插入分区表且分区内排序
df.sortWithinPartitions("category").writeTo("hadoop_prod.default.table1").append()

5)维护表

Ⅰ、获取table对象
//(1)HadoopCatalog
import org.apache.hadoop.conf.Configuration;
import org.apache.iceberg.hadoop.HadoopCatalog;
import org.apache.iceberg.Table;
import org.apache.iceberg.catalog.TableIdentifier;

val conf = new Configuration()
val catalog = new HadoopCatalog(conf,"hdfs://node49:9000/iceberg")
val table: Table = catalog.loadTable(TableIdentifier.of("default","table1"))
//(2)HiveCatalog
import org.apache.iceberg.hive.HiveCatalog;
import org.apache.iceberg.Table;
import org.apache.iceberg.catalog.TableIdentifier;
import spark.implicits._
import collection.JavaConverters._
import collection.mutable._
val catalog = new HiveCatalog()
catalog.setConf(spark.sparkContext.hadoopConfiguration)
val properties = new java.util.HashMap[String, String]
properties.put("warehouse", "hdfs://node49:9000/user/hive/warehouse")
properties.put("uri", "thrift://node86:9083")

catalog.initialize("hive_prod", properties)
val table: Table = catalog.loadTable(TableIdentifier.of("default", "sample1"))
Ⅱ、快照过期清理
// 每次写入Iceberg表都会创建一个表的新快照或版本。快照可以用于时间旅行查询,或者可以将表回滚到任何有效的快照。建议设置快照过期时间,过期的旧快照将从元数据中删除(不再可用于时间旅行查询)。
// 1天过期时间
val tsToExpire: Long = System.currentTimeMillis() - (1000 * 60 * 60 * 24)

table.expireSnapshots().expireOlderThan(tsToExpire).commit()
//或使用SparkActions来设置过期:
//SparkActions可以并行运行大型表的表过期设置(SparkActions不可用,忽略)
//SparkActions.get().expireSnapshots(table).expireOlderThan(tsToExpire).execute()
Ⅲ、删除无效文件
//在Spark和其他分布式处理引擎中,任务或作业失败可能会留下未被表元数据引用的文件,在某些情况下,正常的快照过期可能无法确定不再需要并删除该文件。(SparkActions不可用,忽略)
//SparkActions.get().deleteOrphanFiles(table).execute()
Ⅳ)合并小文件
//数据文件过多会导致更多的元数据存储在清单文件中,而较小的数据文件会导致不必要的元数据量和更低效率的文件打开成本。(SparkActions不可用,忽略)
//SparkActions.get().rewriteDataFiles(table).filter(Expressions.equal("category", "a")).option("target-file-size-bytes", 1024L.toString).execute()

flink集成iceberg

一、Iecberg相关配置

1、相关jar导入

cp antlr-runtime-3.5.2.jar /home/work/software/flink/lib/
cp flink-sql-connector-hive-2.3.6_2.11-1.11.0.jar /home/work/software/flink/lib/
cp hive-exec-2.3.9-core.jar /home/work/software/flink/lib/
cp iceberg-flink-runtime-0.12.0.jar /home/work/software/flink/lib/
cp libfb303-0.9.3.jar /home/work/software/flink/lib/

2、配置文件修改

1)flink-conf.yaml

设置状态后端的相关配置

  • vim /home/work/software/flink/conf/flink-conf.yaml
classloader.check-leaked-classloader: false

state.backend: rocksdb
execution.checkpointing.interval: 30000
# 需要有存储记录状态值,注意修改
state.checkpoints.dir: hdfs://node49:9000/ckps
state.backend.incremental: true

2)配置默认catalog

配置默认的catalog,主要配置了hive-catalog和hadoop-catalog

  • vim /home/work/software/flink/conf/sql-client-defaults.yaml

    修改catalogs配置

catalogs:  # empty list
# A typical catalog definition looks like:
  - name: hive_catalog
    type: iceberg
    catalog-type: hive
    uri: shrift://node86:9083
    clients: 5
    property-version: 1
    warehouse: hdfs://node49:9000/user/hive/warehouse

  - name: hadoop_catalog
    type: iceberg
    catalog-type: hadoop
    warehouse: hdfs://node49:9000/iceberg
    property-version: 1

二、启动sqlClients

1、启动yarn-session后台执行

su work
/home/work/software/flink/bin/yarn-session.sh

2、启动sql-clients

su work
/home/work/software/flink/bin/sql-client.sh embedded -s yarn-session

三、flinkSql常规操作

1、catalog操作

-- hive catalog
CREATE CATALOG hadoop_catalog1 WITH (
  'type'='iceberg',
  'catalog-type'='hadoop',
  'warehouse'='hdfs://node49:9000/iceberg',
  'property-version'='1'
);

-- hadoop catalog
CREATE CATALOG hive_catalog1 WITH (
  'type'='iceberg',
  'catalog-type'='hive',
  'uri'='thrift://node86:9083',
  'clients'='5',
  'property-version'='1',
  'warehouse'='hdfs://node49:9000/user/hive/warehouse'
);

-- 查看catalog
show catalogs;

-- 使用catalog
use catalog hive_catalog;

2、DDL语句

1)创建数据库

-- 创建库
create database flinkdb;
-- 查询库
show databases;
-- 切换库
use flinkdb;

2)创建表

-- 基础表
CREATE TABLE `hadoop_catalog`.`flinkdb`.`sample` (
    id BIGINT COMMENT 'unique id',
    data STRING
);
-- 分区表
CREATE TABLE `hive_catalog`.`flinkdb`.`sample` (
    id BIGINT COMMENT 'unique id',
    data STRING
) PARTITIONED BY (data);
-- 复制结构建表
CREATE TABLE  `hive_catalog`.`flinkdb`.`sample_like` LIKE `hive_catalog`.`flinkdb`.`sample`;
--建表指定catalog
CREATE TABLE flink_table (
    id   BIGINT,
    data STRING
) WITH (
    'connector'='iceberg',
    'catalog-name'='hive_prod',
    'uri'='thrift://node86:9083',
    'warehouse'='hdfs://node49:9000/user/hive/warehouse'
);

--支持upsert命令的表结构
CREATE TABLE `hive_catalog`.`flinkdb`.`flink2` (
  `id`  INT UNIQUE COMMENT 'unique id',
  `data` STRING NOT NULL,
 PRIMARY KEY(`id`) NOT ENFORCED
) with ('format-version'='2', 'write.upsert.enabled'='true');

3)修改表

-- 1)修改表属性
ALTER TABLE `hive_catalog`.`flinkdb`.`sample` SET ('write.format.default'='avro');
-- 2)修改表名 hadoop catalog不支持修改表名
ALTER TABLE `hive_catalog`.`flinkdb`.`sample` RENAME TO `hive_catalog`.`default`.`new_sample`;

4)删除表

DROP TABLE `hive_catalog`.`flinkdb`.`new_sample`;

3、DML语句

1)插入语句

-- 基础插入
INSERT INTO `hive_catalog`.`flinkdb`.`sample` VALUES (1, 'a');
-- 表查询结果插入
INSERT INTO `hive_catalog`.`flinkdb`.`sample_like` SELECT id, data from `hive_catalog`.`flinkdb`.`sample`;
-- 基础批量插入数据
INSERT INTO flink_table VALUES (1, 'AAA'), (2, 'BBB'), (3, 'CCC');

--不可用:报错无界数据流不支持overwrite
INSERT OVERWRITE flink_table VALUES (5, 'a');

-- upsert(没看出upsert)
INSERT INTO flink2 /*+ OPTIONS('upsert-enabled'='true') */ values(1,'e');

2)查询语句

-- 表结构返回查询结果
SET execution.result-mode=tableau;
SELECT * FROM hive_catalog.flinkdb.flink2;


-- 批量读
SET execution.runtime-mode = batch;
SELECT * FROM hive_catalog.flinkdb.flink2;

-- 流式读:任务在yarn-session客户端上不会中止
SET execution.runtime-mode = streaming;
SET table.dynamic-table-options.enabled=true;
SELECT * FROM hive_catalog.flinkdb.flink2 /* +options('streaming'='true','minitor-interval'='1s') */;

四、datasteam操作(见demo代码)