データレイク Iceberg の導入と使用 (統合 Hive、SparkSQL、FlinkSQL)

記事ディレクトリ

導入

概要

データストレージとコンピューティングエンジン間の適応の問題を解決するために、NetflixはIcebergを開発し、2018年11月16日にApacheインキュベーターに入り、2020年5月19日にインキュベーターを卒業し、Apacheのトップレベルプロジェクトとなった。

Iceberg は、大規模なデータ分析シナリオ用のオープン テーブル フォーマット (テーブル フォーマット)です。テーブル形式は、コンピューティング フレームワーク (Flink、Spark...) の下で、データ ファイルの上にメタデータとデータ ファイルを編成する方法として理解できます

効果

ビッグデータ分野は長い開発と探究を経て、ビッグデータ技術の出現と反復により、ユーザーが大量のデータを処理する敷居は低くなりましたが、データ形式の適応という無視できない問題があります。さまざまなエンジンに。

つまり、計算に異なるエンジンを使用する場合、エンジンに応じてデータを調整する必要があります。これはかなり難しい質問です。

この目的のために、上位のコンピューティング エンジンと基盤となるストレージ フォーマットの間の中間層という新しいソリューションが登場しました。この中間層はデータの保存方法ではなく、データのメタデータ編成方法を定義するだけであり、従来のデータベースの「テーブル」に似た統一されたセマンティクスをエンジン レベルに提供します。その基礎となる層は依然として Parquet や ORC などのストレージ形式です。これに基づいて、Netflix は Iceberg を開発しました。これは、現在 Apache のトップレベルのプロジェクトです。

特性

データストレージおよびコンピューティングエンジンプラグイン

Iceberg は、特定のデータ ストレージや計算エンジンに縛られない、オープンでユニバーサルなテーブル フォーマット実装ソリューションを提供します。現在、ビッグデータ分野の一般的なデータ ストレージ (HDFS、S3...) およびコンピューティング エンジン (Flink、Spark...) を Iceberg に接続できます。

実稼働環境では、さまざまなコンポーネントを選択して一緒に使用できます。計算エンジンを経由せずに、ファイル システムに保存されているデータを直接読み取ることもできます。

リアルタイムストリーミングとバッチ統合

Iceberg 上流コンポーネントがデータの書き込みを完了すると、下流コンポーネントは時間内にそのデータを読み取り、クエリを実行できるようになります。リアルタイムのシナリオに対応できます。また、Iceberg はストリーム/バッチ読み取りインターフェイスとストリーム/バッチ書き込みインターフェイスも提供します。ストリーム データとバッチ データを同じプロセスで同時に処理できるため、ETL リンクが大幅に簡素化されます。

データパフォーマンス (テーブル進化)

Iceberg は、SQL を通じてテーブルレベルのスキーマ進化を実行できます。これらの操作を行う場合、コストは非常に低くなります。データの読み取り、再書き込み、移行といった時間と労力のかかる操作は必要ありません。

たとえば、一般的に使用されている Hive で、日ごとにパーティション化されたテーブルを時間ごとにパーティション化するテーブルに変更する必要がある場合です。現時点では、元のテーブルを直接変更することはできません。新しい時間単位のパーティション テーブルを作成し、その新しい時間単位のパーティション テーブルにデータを挿入することのみが可能です。また、Rename コマンドを使用して新しいテーブルの名前を元のテーブルに変更し、元のテーブルの上位層アプリケーションを使用する場合でも、パーティション フィールドの変更により SQL の変更が必要になる場合があります。とても面倒です。

スキーマの進化

Iceberg は次の進化モードをサポートしています。

  • ADD: テーブルまたはネストされた構造に新しい列を追加します。

  • ドロップ: テーブルまたはネストされた構造から列を削除します。

  • 名前の変更: テーブルまたはネストされた構造内の列の名前を変更します。

  • 更新: tinyint を int に変更するなど、複雑な構造 (struct、map<key, value>、list) の基本型の型長を拡張します。

  • 並べ替え: ネストされた構造内の列またはフィールドの順序を変更します。

Iceberg は、Schema Evolution が副作用のない独立した操作プロセスであるメタデータ操作であり、データ ファイルの書き換えプロセスを含まないことを保証します。詳細は以下のとおりです。

  • 列を追加する場合、既存のデータは別の列から読み取られません。

  • ネストされた構造内の列またはフィールドを削除しても、他の列の値は変更されません。

  • ネストされた構造内の列またはフィールドを更新しても、他の列の値は変更されません。

  • ネストされた構造内の列またはフィールドの順序を変更しても、関連付けられた値は変更されません。

Iceberg は、一意の ID を使用して、テーブル内の各列の情報を見つけます。列が追加されると、新しい一意の ID がその列に割り当てられ、すでに使用されている ID は使用されません。

名前または位置情報を使用して列を検索すると、いくつかの問題が発生します。たとえば、名前を使用すると、名前が繰り返される可能性があります。位置を使用すると、順序を変更できず、古いフィールドを削除できません。 。

パーティションの進化

Iceberg のクエリ プロセスはパーティション情報に直接関連していないため、Iceberg は既存のテーブルを直接変更できます。

テーブルのパーティション化戦略を変更する場合、変更されたパーティションの前のデータは変更されません。古いパーティション化戦略は引き続き使用され、新しいデータは新しいパーティション化戦略を使用します。つまり、同じテーブルには次のような変更が加えられます。 2 つのパーティショニング戦略。古いデータは古いパーティショニング戦略を採用し、新しいデータは新しいパーティショニング戦略を採用します。メタデータでは、2 つのパーティショニング戦略は互いに独立しており、重複しません。

データをクエリするときに、クロスパーティション戦略がある場合、Iceberg の公式 Web サイトで提供されている図に示すように、データは 2 つの異なる実行プランに解析されます。

MMSIZE

図のbooking_tableテーブルは、2008年に月ごとにパーティション化され、2009年に入ってから日ごとのパーティションに変更されました。この中程度のパーティション化戦略がこのテーブルに共存しています。

Iceberg の Hidden Partition を使用すると、SQL クエリを作成するときに、SQL でパーティション フィルター条件を指定する必要がなく、Iceberg が自動的にパーティションを作成し、不要なデータをフィルターで除外します。

Iceberg パーティション展開操作もメタデータ操作であり、データ ファイルは書き換えられません。

ソート順序の進化

Iceberg は、既存のテーブルの並べ替え戦略を変更できます。並べ替え戦略を変更した後も、古いデータでは古い並べ替え戦略が引き続き使用されます。Iceberg にデータを書き込む計算エンジンは常に最新の並べ替え戦略を選択しますが、並べ替えのコストが非常に高い場合、並べ替えは実​​行されません。

隠しパーティション

Iceberg のパーティション情報は手動メンテナンスを必要とせず、非表示にすることができます。Hive に似た他のパーティション戦略とは異なり、Iceberg のパーティション フィールド/戦略 (特定のフィールドから計算) は、テーブルのフィールドまたはテーブル データ ストレージ ディレクトリである必要はありません。 . それは関係ありません。テーブルを作成するか、パーティション分割戦略を変更すると、新しいデータが属するパーティションが自動的に計算されます。クエリを実行する場合、リレーショナル テーブルのパーティション化にどのフィールドや戦略が使用されているかを知る必要はありません。ビジネス ロジックに集中するだけで済みます。Iceberg は不要なパーティション データを自動的にフィルタリングします。

Icebergのパーティション情報とテーブルデータ格納ディレクトリが独立しているからこそ、Icebergのテーブルパーティションの変更が可能となり、不和にはデータ移行が伴う。

ミラーデータクエリ(タイムトラベル)

Iceberg は、クエリ テーブルの履歴の特定の時点でデータ ミラーリング (スナップショット) をクエリする機能を提供します。この機能により、最新の SQL ロジックを履歴データに適用できます。

サポートトランザクション (ACID)

トランザクション (ACID) メカニズムを提供することにより、Iceberg には更新/挿入機能があり、書き込み中の読み取りが可能になり、ダウンストリーム コンポーネントによるデータの消費が高速化されます。トランザクションにより、ダウンストリーム コンポーネントはコミットされたデータのみを使用でき、部分的なデータやコミットされていないデータさえも読み取らないことが保証されます。

楽観的ロックに基づく同時実行のサポート

Iceberg はオプティミスティック ロックに基づいて、複数のプログラムが同時に書き込みを行う機能を提供し、データの線形一貫性を保証します。

ファイルレベルのデータプルーニング

Iceberg のメタデータは、最大値、最小値、カウント数など、各データ ファイルの統計情報を提供します。したがって、従来のパーティショニングや列フィルタリングに加えて、SQL クエリのフィルタリング条件をファイル レベルにまでプッシュダウンすることができ、クエリ効率が大幅に向上します。

他のデータレイクフレームワークの比較

MMSIZE

MMSIZE

収納構造

MMSIZE

MMSIZE

データファイルデータファイル

データ ファイルは、Apache Iceberg テーブルが実際にデータを保存するファイルです。通常、テーブルのデータ ストレージ ディレクトリのデータ ディレクトリにあります。ファイル形式が parquet の場合、ファイルは「.parquet」で終わります。

例: 00000-0-atguigu_20230203160458_22ee74c9-643f-4b27-8fc1-9cbd5f64dad4-job_1675409881387_0007-00001.parquet はデータ ファイルです。

Iceberg は更新ごとに複数のデータ ファイルを生成します。

テーブルのスナップショット スナップショット

スナップショットは、特定の時点におけるテーブルの状態を表します。各スナップショットには、特定の時点でのテーブルのすべてのデータ ファイルのリストが含まれています。データ ファイルはさまざまなマニフェスト ファイルに保存され、マニフェスト ファイルはマニフェスト リスト ファイルに保存され、マニフェスト リスト ファイルはスナップショットを表します。

マニフェストリスト マニフェストリスト

マニフェスト リストは、テーブル スナップショットを構築するためのマニフェスト ファイルをリストしたメタデータ ファイルです。このメタデータ ファイルにはマニフェスト ファイルのリストが保存されており、各マニフェスト ファイルは 1 行を占めます。各行には、マニフェストファイルのパス、格納されているデータファイル(データファイル)のパーティション範囲、追加されたデータファイルの数、削除されたデータファイルの数などの情報が格納されており、これらの情報を利用して、クエリ時のフィルタリング。

例: snap-6746266566064388720-1-52f2f477-2585-4e69-be42-bbad9a46ed17.avro はマニフェスト リスト ファイルです。

マニフェストファイル

マニフェスト ファイルは、スナップショットを構成するデータ ファイルのリスト情報をリストしたメタデータ ファイルでもあります。各行は、データ ファイルのステータス、ファイル パス、パーティション情報、列レベルの統計情報 (各列の最大値と最小値、NULL 値の数、など)、ファイルのサイズ、データ行数などの情報。列レベルの統計により、テーブル データをスキャンするときに不要なファイルを除外できます。

マニフェスト ファイルは、接尾辞「.avro」で終わる avro 形式で保存されます (例: 52f2f477-2585-4e69-be42-bbad9a46ed17-m0.avro)。

ハイブとの統合

環境整備

(1) HiveとIcebergのバージョン対応は以下の通りです。

ハイブバージョン 公式に推奨される Hive バージョン 氷山バージョン
2.x 2.3.8 0.8.0-インキュベート – 1.1.0
3.x 3.1.2 0.10.0 – 1.1.0

Iceberg と Hive 2 および Hive 3.1.2/3 の統合により、次の機能がサポートされます。

  • テーブルの作成

  • テーブルの削除

  • テーブルの読み取り

  • テーブルにINSERT

さらに多くの機能をサポートするには、Hive 4.x (現在のアルファ バージョン) が必要です。

(2) jarパッケージをアップロードし、Hiveのauxlibディレクトリにコピーします。

mkdir auxlib
cp iceberg-hive-runtime-1.1.0.jar /opt/module/hive/auxlib
cp libfb303-0.9.3.jar /opt/module/hive/auxlibcp iceberg-hive-runtime-1.1.0.jar /opt/module/hive/auxlibcp libfb303-0.9.3.jar /opt/module/hive/auxlib

(3) 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>

TEZ エンジンを使用する場合の注意事項:

  • Hive バージョン >=3.1.2 を使用し、TEZ バージョン >=0.10.1 が必要です

  • tez 更新構成を指定します。

    <property>
        <name>tez.mrreader.config.update.properties</name>
        <value>hive.io.file.readcolumn.names,hive.io.file.readcolumn.ids</value>
    </property>
    
  • Iceberg 0.11.0 以降、Hive が Tez エンジンを使用する場合は、ベクトル化の実行をオフにする必要があります。

    <property>
        <name>hive.vectorized.execution.enabled</name>
        <value>false</value>
    </property>
    

(4) HMSサービスの開始

(5) Hadoopの起動

カタログの作成と管理

Iceberg は、Hive、Hadoop、Amazon の AWS Glue、カスタム カタログなど、さまざまなカタログ タイプをサポートしています。

さまざまな構成に応じて、次の 3 つの状況が考えられます。

  • Iceberg.catalog が設定されていないため、HiveCatalog がデフォルトで使用されます
設定項目 説明する
Iceberg.catalog.<カタログ名>.type カタログ タイプ: ハイブ、Hadoop、カスタム カタログを使用する場合は設定されません
Iceberg.catalog.<カタログ名>.catalog-impl カタログ実装クラス。上記のタイプが設定されていない場合、このパラメータを設定する必要があります
Iceberg.catalog.<カタログ名>.<キー> カタログのその他の設定項目
  • 次の表に示すように、指定されたカタログ タイプを使用して、iceberg.catalog のタイプが設定されます。

  • 指定されたルート パスを介して直接 Iceberg テーブルをロードするには、iceberg.catalog=location_based_table を設定します。

HiveCatalog がデフォルトで使用されます

CREATE TABLE iceberg_test1 (i int) STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler';
 
INSERT INTO iceberg_test1 values(1);

HDFS を見ると、テーブル ディレクトリがデフォルトのハイブ ウェアハウス パスの下にあることがわかります。

カタログの種類を指定します

(1) HiveCatalogの利用

set iceberg.catalog.iceberg_hive.type=hive;
set iceberg.catalog.iceberg_hive.uri=thrift://hadoop1:9083;
set iceberg.catalog.iceberg_hive.clients=10;
set iceberg.catalog.iceberg_hive.warehouse=hdfs://hadoop1:8020/warehouse/iceberg-hive;

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

(2)使用 HadoopCatalog

set iceberg.catalog.iceberg_hadoop.type=hadoop;
set iceberg.catalog.iceberg_hadoop.warehouse=hdfs://hadoop1:8020/warehouse/iceberg-hadoop;

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

INSERT INTO iceberg_test3 values(1);

ロードするパスを指定します

Iceberg 形式のテーブルが HDFS にすでに存在する場合は、Hive で Iceberg 形式のテーブルを作成することで、対応するロケーション パス マッピング データを指定できます。

CREATE EXTERNAL TABLE iceberg_test4 (i int)
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
LOCATION 'hdfs://hadoop1:8020/warehouse/iceberg-hadoop/default/iceberg_test3'
TBLPROPERTIES ('iceberg.catalog'='location_based_table');

基本操作

テーブルの作成

(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;

Hive 構文はパーティション テーブルを作成します。これは HMS にパーティションを作成しませんが、パーティション データを Iceberg ID パーティションに変換します。この場合、Iceberg のパーティション変換は使用できません (例: days (タイムスタンプ))。Iceberg 形式テーブルのパーティション変換を使用してパーティションを識別する場合は、Spark または Flink エンジンを使用してテーブルを作成する必要があります。 。

テーブルの変更

テーブル属性の変更は HiveCatalog テーブルのみがサポートされており、Iceberg テーブル属性と Hive テーブル属性は HMS に同期して保存されます。

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

テーブルの挿入

標準の単一テーブルの INSERT INTO 操作をサポートします。

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

HIVE 3.xではINSERT OVERWRITEが実行できるようになりましたが、実際には追加です。

テーブルの削除

DROP TABLE iceberg_create1;

Spark SQLとの統合

環境整備

(1) Sparkのインストール

1) SparkとIcebergのバージョン対応は以下の通りです。

スパークバージョン 氷山バージョン
2.4 0.7.0-インキュベート – 1.1.0
3.0 0.9.0 – 1.0.0
3.1 0.12.0 – 1.1.0
3.2 0.13.0 – 1.1.0
3.3 0.14.0 – 1.1.0

2) Spark インストール パッケージをアップロードして解凍します。

tar -zxvf spark-3.3.1-bin-hadoop3.tgz -C /opt/module/
mv /opt/module/spark-3.3.1-bin-hadoop3 /opt/module/spark-3.3.1

3) 環境変数を設定する

sudo vim /etc/profile.d/my_env.sh

export SPARK_HOME=/opt/module/spark-3.3.1
export PATH=$PATH:$SPARK_HOME/bin

source /etc/profile.d/my_env.sh

4) Iceberg の jar パッケージを Spark の jar ディレクトリにコピーします

cp /opt/software/iceberg/iceberg-spark-runtime-3.3_2.12-1.1.0.jar /opt/module/spark-3.3.1/jars

(2) Hadoopを起動する

Spark構成カタログ

Spark は、hive と hadoop の 2 つのカタログ設定をサポートします。Hive カタログは、Iceberg テーブル ストレージに Hive のデフォルト データ パスを使用します。Hadoop カタログでは、Iceberg 形式のテーブル ストレージ パスを指定する必要があります。

vim 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://hadoop1:9083

use hive_prod.db;

Hadoopカタログ

spark.sql.catalog.hadoop_prod = org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.hadoop_prod.type = hadoop
spark.sql.catalog.hadoop_prod.warehouse = hdfs://hadoop1:8020/warehouse/spark-iceberg

use hadoop_prod.db;

SQL操作

テーブルの作成

use hadoop_prod;
create database default;
use default;

CREATE TABLE hadoop_prod.default.sample1 (
    id bigint COMMENT 'unique id',
    data string)
USING iceberg
  • PARTITIONED BY (partition-expressions) : パーティションを構成します

  • LOCATION '(full-qualified-uri)' : テーブルのパスを指定します

  • COMMENT 'テーブルドキュメント': 構成テーブルの備考

  • TBLPROPERTIES ('key'='value', …) : 構成テーブルのプロパティ

テーブルのプロパティ: https://iceberg.apache.org/docs/latest/configuration/

Iceberg テーブルに変更を加えるたびに、アトミック性を提供するために新しいメタデータ ファイル (json ファイル) が生成されます。デフォルトでは、古いメタデータ ファイルは履歴ファイルとして保存され、削除されません。

メタデータ ファイルを自動的にクリアする場合は、テーブル プロパティで write.metadata.delete-after-commit.enabled=true を設定します。これにより、一部のメタデータ ファイルが (write.metadata.previous-versions-max まで) 保持され、新しくメタデータ ファイルが作成されるたびに古いメタデータ ファイルが削除されます。

(1) パーティションテーブルの作成

1) パーティションテーブル

CREATE TABLE hadoop_prod.default.sample2 (
    id bigint,
    data string,
    category string)
USING iceberg
PARTITIONED BY (category)

2) 隠しパーティションテーブルを作成する

CREATE TABLE hadoop_prod.default.sample3 (
    id bigint,
    data string,
    category string,
    ts timestamp)
USING iceberg
PARTITIONED BY (bucket(16, id), days(ts), category)

サポートされている変換は次のとおりです。

  • 年(ts): 年で割ったもの

  • 月(ts): 月で割った値

  • days(ts) または date(ts): dateint パーティションと同等

  • hours(ts) または date_hour(ts): dateint および時間のパーティショニングと同等

  • bucket(N,col): mod N バケットをハッシュ値で除算します。

  • truncate(L,col): L に切り捨てられた値で除算します。

文字列は指定された長さに切り詰められます

整数とlong型はbinに切り捨てられます。truncate(10, i)はパーティション0、10、20、30、…を生成します。

(2) CTAS 構文を使用してテーブルを作成する

CREATE TABLE hadoop_prod.default.sample4
USING iceberg
AS SELECT * from hadoop_prod.default.sample3

パーティションを指定しない場合、パーティションは存在しません。パーティションとテーブルの属性を再指定する必要があります。

CREATE TABLE hadoop_prod.default.sample5
USING iceberg
PARTITIONED BY (bucket(8, id), hours(ts), category)
TBLPROPERTIES ('key'='value')
AS SELECT * from hadoop_prod.default.sample3

(3) Replace tableを利用してテーブルを作成する

REPLACE TABLE hadoop_prod.default.sample5
USING iceberg
AS SELECT * from hadoop_prod.default.sample3

REPLACE TABLE hadoop_prod.default.sample5
USING iceberg
PARTITIONED BY (part)
TBLPROPERTIES ('key'='value')
AS SELECT * from hadoop_prod.default.sample3


CREATE OR REPLACE TABLE hadoop_prod.default.sample6
USING iceberg
AS SELECT * from hadoop_prod.default.sample3

テーブルの削除

HadoopCatalog の場合、DROP TABLE を実行すると、カタログからテーブルが削除され、テーブルの内容が削除されます。

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 を実行するとカタログからテーブルが削除され、テーブルの内容が削除されていました。

  • 0.14 以降、DROP TABLE はカタログからテーブルのみを削除し、データは削除しません。テーブルの内容を削除するには、DROP table PURGE を使用する必要があります。

CREATE TABLE hive_prod.default.sample7 (
    id bigint COMMENT 'unique id',
    data string)
USING iceberg

INSERT INTO hive_prod.default.sample7 values(1,'a')

(1) テーブルの削除

DROP TABLE hive_prod.default.sample7

(2) テーブルとデータの削除

DROP TABLE hive_prod.default.sample7 PURGE

テーブルの変更

Iceberg は、Spark 3 で以下を含む ALTER TABLE を完全にサポートしています。

  • テーブル名の変更

  • テーブルのプロパティを設定または削除する

  • 列の追加、削除、名前変更

  • ネストされたフィールドの追加、削除、名前変更

  • 最上位の列とネストされた構造フィールドの順序を変更する

  • int、float、および 10 進数フィールドのタイプを拡張します

  • 必須列をオプション列に変更する

さらに、SQL 拡張機能を使用して、パーティション進化のサポートを追加し、テーブルの書き込み順序を設定することができます。

CREATE TABLE hive_prod.default.sample1 (
    id bigint COMMENT 'unique id',
    data string)
USING iceberg

(1) テーブル名の変更(HadoopCatalogのテーブル名の変更は未サポートです)

ALTER TABLE hive_prod.default.sample1 RENAME TO hive_prod.default.sample2

(2) テーブル属性の変更

  • テーブルのプロパティを変更する

    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.'
    )
    
  • テーブル属性の削除

    ALTER TABLE hive_prod.default.sample1 UNSET TBLPROPERTIES ('read.split.target-size')
    

(3) 列を追加する

ALTER TABLE hadoop_prod.default.sample1
ADD COLUMNS (
    category string comment 'new_column'
)

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

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

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

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

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

-- 在Map类型的value的struct中添加一个字段。
ALTER TABLE hadoop_prod.default.sample1
ADD COLUMN pointsm.value.b int

Spark 2.4.4 以降では、FIRST 句または AFTER 句を追加することでどこにでも列を追加できます。

ALTER TABLE hadoop_prod.default.sample1
ADD COLUMN new_column1 bigint AFTER id

ALTER TABLE hadoop_prod.default.sample1
ADD COLUMN new_column2 bigint FIRST

(4) 列を変更する

  • 列名の変更

    ALTER TABLE hadoop_prod.default.sample1 RENAME COLUMN data TO data1
    
  • 列変更タイプ (安全な変換のみが許可されます)

    ALTER TABLE hadoop_prod.default.sample1
    ADD COLUMNS (
        idd int
      )
    ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN idd TYPE bigint
    
  • Alter Column 列のコメントを変更します。

    ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN id TYPE double COMMENT 'a'
    ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN id COMMENT 'b'
    
  • 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
    
  • Alter Column は、列を null にすることが許可されるかどうかを変更します。

    ALTER TABLE hadoop_prod.default.sample1 ALTER COLUMN id DROP NOT NULL
    

    ALTER COLUMN は構造体の型の更新には使用されません。構造体型のフィールドを追加または削除するには、ADD COLUMN および DROP COLUMN を使用します。

(5) 列の削除

ALTER TABLE hadoop_prod.default.sample1 DROP COLUMN idd
ALTER TABLE hadoop_prod.default.sample1 DROP COLUMN point.z

(6) パーティションの追加 (Spark3、拡張子の設定が必要)

vim spark-default.conf
spark.sql.extensions = org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions

再度、spark-sql シェルに入ります。

ALTER TABLE hadoop_prod.default.sample1 ADD PARTITION FIELD category 

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

ALTER TABLE hadoop_prod.default.sample1 ADD PARTITION FIELD bucket(16, id) AS shard

(7) パーティションの削除(Spark3、拡張機能の設定が必要)

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

パーティションを削除しても、テーブル構造には列がまだ存在していることに注意してください。

パーティション フィールドの削除はメタデータ操作であり、既存のテーブル データは変更されません。新しいデータは新しいパーティションに書き込まれますが、既存のデータは古いパーティション レイアウトに残ります。

パーティションが変更されると、動的パーティションのオーバーライド動作も変更されます。たとえば、日単位ではなく時間単位でパーティションを作成した場合、オーバーライドによって時間単位のパーティションがカバーされますが、日単位のパーティションはオーバーライドされなくなります。

パーティション フィールドを削除する場合は注意してください。メタデータ クエリが失敗したり、異なる結果が生成されたりする可能性があります。

(8) パーティションを変更する (Spark3、拡張子の設定が必要)

ALTER TABLE hadoop_prod.default.sample1 REPLACE PARTITION FIELD bucket(16, id) WITH bucket(8, id)

(9) テーブルの書き込み順を変更する

ALTER TABLE hadoop_prod.default.sample1 WRITE ORDERED BY category, id
ALTER TABLE hadoop_prod.default.sample1 WRITE ORDERED BY category ASC, id DESC
ALTER TABLE hadoop_prod.default.sample1 WRITE ORDERED BY category ASC NULLS LAST, id DESC NULLS FIRST

テーブルの書き込み順序は、クエリのデータ順序を保証するものではありません。データがテーブルに書き込まれる方法にのみ影響します。

WRITE ORDERED BY はグローバルな順序付けを設定します。つまり、INSERT コマンドで ORDER BY を使用するのと同じように、タスク全体で行を順序付けします。

INSERT INTO hadoop_prod.default.sample1
SELECT id, data, category, ts FROM another_table
ORDER BY ts, category

タスク間ではなく、各タスク内で順序付けするには、ローカルの ORDERED BY を使用します。

ALTER TABLE hadoop_prod.default.sample1 WRITE LOCALLY ORDERED BY category, id

(10) パーティション単位の並列書き込み

ALTER TABLE hadoop_prod.default.sample1 WRITE DISTRIBUTED BY PARTITION
ALTER TABLE hadoop_prod.default.sample1 WRITE DISTRIBUTED BY PARTITION LOCALLY ORDERED BY category, id

データの挿入

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 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)

クエリデータ

(1) 通常のクエリ

SELECT count(1) as count, data
FROM local.db.table
GROUP BY data

(2) クエリメタデータ

// 查询表快照
SELECT * FROM hadoop_prod.default.a.snapshots

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

// 查询表历史
SELECT * FROM hadoop_prod.default.a.history

// 查询 manifest
ELECT * FROM hadoop_prod.default.a.manifests

ストアドプロシージャ

プロシージャは、CALL を介して、設定された Iceberg カタログから使用できます。すべてのプロシージャは名前空間内にあります。

(1)文法

パラメータを名前で渡します

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)

(2) スナップショット管理

  • 指定されたスナップショット ID にロールバックします

    CALL hadoop_prod.system.rollback_to_snapshot('default.a', 7601163594701794741)
    
  • 指定した時刻のスナップショットにロールバックする

    CALL hadoop_prod.system.rollback_to_timestamp('db.sample', TIMESTAMP '2021-06-30 00:00:00.000')
    
  • テーブルの現在のスナップショット ID を設定します。

    CALL hadoop_prod.system.set_current_snapshot('db.sample', 1)
    
  • スナップショットから現在のテーブル状態への変更

    CALL hadoop_prod.system.cherrypick_snapshot('default.a', 7629160535368763452)
    CALL hadoop_prod.system.cherrypick_snapshot(snapshot_id => 7629160535368763452, table => 'default.a' )
    

(3) メタデータ管理

  • 指定した日時より古いスナップショットを削除しますが、最新の 100 個のスナップショットは保持します。

    CALL hive_prod.system.expire_snapshots('db.sample', TIMESTAMP '2021-06-30 00:00:00.000', 100)
    
  • Iceberg テーブル内のどのメタデータ ファイルでも参照されていないファイルを削除する

    #列出所有需要删除的候选文件
    CALL catalog_name.system.remove_orphan_files(table => 'db.sample', dry_run => true)
    #删除指定目录中db.sample表不知道的任何文件
    CALL catalog_name.system.remove_orphan_files(table => 'db.sample', location => 'tablelocation/data')
    
  • データファイルのマージ(小さいファイルのマージ)

    CALL catalog_name.system.rewrite_data_files('db.sample')
    CALL catalog_name.system.rewrite_data_files(table => 'db.sample', strategy => 'sort', sort_order => 'id DESC NULLS LAST,name ASC NULLS FIRST')
    CALL catalog_name.system.rewrite_data_files(table => 'db.sample', strategy => 'sort', sort_order => 'zorder(c1,c2)')
    CALL catalog_name.system.rewrite_data_files(table => 'db.sample', options => map('min-input-files','2'))
    CALL catalog_name.system.rewrite_data_files(table => 'db.sample', where => 'id = 3 and name = "foo"')
    
  • テーブルリストを書き換えて実行計画を最適化する

    CALL catalog_name.system.rewrite_manifests('db.sample')
    
    #重写表db中的清单。并禁用Spark缓存的使用。这样做可以避免执行程序上的内存问题。
    CALL catalog_name.system.rewrite_manifests('db.sample', false)
    

(4) 移行テーブル

  • スナップショット

    CALL catalog_name.system.snapshot('db.sample', 'db.snap')
    CALL catalog_name.system.snapshot('db.sample', 'db.snap', '/tmp/temptable/')
    
  • 移行する

    CALL catalog_name.system.migrate('spark_catalog.db.sample', map('foo', 'bar'))
    CALL catalog_name.system.migrate('db.sample')
    
  • データファイルの追加

    CALL spark_catalog.system.add_files(
        table => 'db.tbl',
        source_table => 'db.src_tbl',
        partition_filter => map('part_col_1', 'A')
    )
    
    CALL spark_catalog.system.add_files(
        table => 'db.tbl',
        source_table => '`parquet`.`path/to/table`'
    )
    

(5) メタデータ情報

  • 指定されたスナップショットの親スナップショット ID を取得します

    CALL spark_catalog.system.ancestors_of('db.tbl')
    
  • 指定されたスナップショットのすべての祖先スナップショットを取得します

    CALL spark_catalog.system.ancestors_of('db.tbl', 1)
    CALL spark_catalog.system.ancestors_of(snapshot_id => 1, table => 'db.tbl')
    

データフレームの操作

環境整備

(1) Mavenプロジェクトの作成とpomファイルの設定

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.atguigu.iceberg</groupId>
    <artifactId>spark-iceberg-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <scala.binary.version>2.12</scala.binary.version>
        <spark.version>3.3.1</spark.version>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- Spark的依赖引入 -->
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_${scala.binary.version}</artifactId>
            <scope>provided</scope>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_${scala.binary.version}</artifactId>
            <scope>provided</scope>
            <version>${spark.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-hive_${scala.binary.version}</artifactId>
            <scope>provided</scope>
            <version>${spark.version}</version>
        </dependency>

        <!--fastjson <= 1.2.80 存在安全漏洞,-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.apache.iceberg/iceberg-spark-runtime-3.3 -->
        <dependency>
            <groupId>org.apache.iceberg</groupId>
            <artifactId>iceberg-spark-runtime-3.3_2.12</artifactId>
            <version>1.1.0</version>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <!-- assembly打包插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <archive>
                        <manifest>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>

            <!--Maven编译scala所需依赖-->
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

(2) カタログの設定

val 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://hadoop1: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://hadoop1:8020/warehouse/spark-iceberg")
  .getOrCreate()

テーブルの読み取り

(1) 荷重表

spark.read
.format("iceberg")
.load("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a")
.show()

または

// 仅支持Spark3.0以上
spark.table("iceberg_hadoop.default.a")
.show()

(2) タイムトラベル: 指定した時刻にクエリを実行する

spark.read
    .option("as-of-timestamp", "499162860000")
    .format("iceberg")
.load("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a")
.show()

(3) タイムトラベル: スナップショット ID を指定してクエリ

spark.read
    .option("snapshot-id", 7601163594701794741L)
    .format("iceberg")
.load("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a")
.show()

(4) インクリメンタルクエリ

spark.read
.format("iceberg")
.option("start-snapshot-id", "10963874102873")
.option("end-snapshot-id", "63874143573109")
.load("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a")
.show()

クエリされたテーブルは追加メソッドでのみデータを書き込むことができ、置換、上書き、削除の操作はサポートされていません。

チェックリスト

(1) クエリメタデータ

spark.read.format("iceberg").load("iceberg_hadoop.default.a.files")
spark.read.format("iceberg").load("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a#files")

(2) メタデータテーブルのタイムトラベルクエリ

spark.read
.format("iceberg")
.option("snapshot-id", 7601163594701794741L)
.load("iceberg_hadoop.default.a.files")

書き込みテーブル

(1) サンプルクラスの作成とDFの準備

case class Sample(id:Int,data:String,category:String)

val df: DataFrame = spark.createDataFrame(Seq(Sample(1,'A', 'a'), Sample(2,'B', 'b'), Sample(3,'C', 'c')))

(2) データを挿入してテーブルを構築する

df.writeTo("iceberg_hadoop.default.table1").create()

import spark.implicits._
df.writeTo("iceberg_hadoop.default.table1")
  .tableProperty("write.format.default", "orc")
  .partitionedBy($"category")
  .createOrReplace()

(3)append追加

df.writeTo("iceberg_hadoop.default.table1").append()

(4) 動的パーティションカバレッジ

df.writeTo("iceberg_hadoop.default.table1").overwritePartitions()

(5) 静的パーティションの適用範囲

import spark.implicits._
df.writeTo("iceberg_hadoop.default.table1").overwrite($"category" === "c")

(6) パーティションテーブルに挿入し、パーティション内でソートする

df.sortWithinPartitions("category")
    .writeTo("iceberg_hadoop.default.table1")
    .append()

メンテナンステーブル

(1) Tableオブジェクトの取得

1)Hadoopカタログ

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://hadoop1:8020/warehouse/spark-iceberg")
val table: Table = catalog.loadTable(TableIdentifier.of("db","table1"))

2)ハイブカタログ

import org.apache.iceberg.hive.HiveCatalog;
import org.apache.iceberg.Table;
import org.apache.iceberg.catalog.TableIdentifier;

val catalog = new HiveCatalog()
catalog.setConf(spark.sparkContext.hadoopConfiguration)

val properties = new util.HashMap[String,String]()
properties.put("warehouse", "hdfs://hadoop1:8020/warehouse/spark-iceberg")
properties.put("uri", "thrift://hadoop1:9083")

catalog.initialize("hive", properties)
val table: Table = catalog.loadTable(TableIdentifier.of("db", "table1"))

(2) スナップショットの有効期限のクリーンアップ

Iceberg テーブルに書き込むたびに、テーブルの新しいスナップショットまたはバージョンが作成されます。スナップショットはタイム トラベル クエリに使用したり、テーブルを有効なスナップショットにロールバックしたりできます。スナップショットの有効期限を設定することをお勧めします。期限切れの古いスナップショットはメタデータから削除されます (タイム トラベル クエリには使用できなくなります)。

// 1天过期时间
val tsToExpire: Long = System.currentTimeMillis() - (1000 * 60 * 60 * 24)

table.expireSnapshots()
  .expireOlderThan(tsToExpire)
  .commit()

または、SparkActions を使用して有効期限を設定します。

//SparkActions可以并行运行大型表的表过期设置
SparkActions.get()
  .expireSnapshots(table)
  .expireOlderThan(tsToExpire)
  .execute()

(3) 無効なファイルを削除する

Spark やその他の分散処理エンジンでは、タスクまたはジョブの失敗により、ファイルがテーブル メタデータによって参照されないままになる場合があり、場合によっては、通常のスナップショットの有効期限によってファイルが不要になったと判断できず、ファイルが削除される場合があります。

SparkActions
    .get()
    .deleteOrphanFiles(table)
    .execute()

(4) 小さいファイルを結合する

データ ファイルが過剰になると、マニフェスト ファイルに保存されるメタデータが増加します。一方、データ ファイルが小さいと、不必要な量のメタデータが発生し、ファイルを開くコストの効率が低下します。

SparkActions
    .get()
    .rewriteDataFiles(table)
    .filter(Expressions.equal("category", "a"))
    .option("target-file-size-bytes", 1024L.toString) //1KB
    .execute()

Flink SQLとの統合

Apache Iceberg は、Apache Flink の DataStream API と Table API の両方をサポートしています。

環境整備

(1) Flinkをインストールする

1) FlinkとIcebergのバージョン対応は以下の通りです

フリンクバージョン 氷山バージョン
1.11 0.9.0 – 0.12.1
1.12 0.12.0~0.13.1
1.13 0.13.0 – 1.0.0
1.14 0.13.0 – 1.1.0
1.15 0.14.0 – 1.1.0
1.16 1.1.0 – 1.1.0

2) Flink インストール パッケージをアップロードして解凍します。

tar -zxvf flink-1.16.0-bin-scala_2.12.tgz -C /opt/module/

3) 環境変数を設定する

sudo vim /etc/profile.d/my_env.sh
export HADOOP_CLASSPATH=`hadoop classpath`
source /etc/profile.d/my_env.sh

4) Iceberg の jar パッケージを Flink の lib ディレクトリにコピーします

cp /opt/software/iceberg/iceberg-flink-runtime-1.16-1.1.0.jar /opt/module/flink-1.16.0/lib

(2) Hadoopを起動する

(3) SQLクライアントを起動する

1) flink-conf.yaml 構成を変更する

vim /opt/module/flink-1.16.0/conf/flink-conf.yaml

classloader.check-leaked-classloader: false
taskmanager.numberOfTaskSlots: 4

state.backend: rocksdb
execution.checkpointing.interval: 30000
state.checkpoints.dir: hdfs://hadoop1:8020/ckps
state.backend.incremental: true

2) ローカルモード

(1) ワーカーの変更

vim /opt/module/flink-1.16.0/conf/workers
#表示:会在本地启动3个TaskManager的 local集群
localhost
localhost
localhost

(2)フリンクを開始する

/opt/module/flink-1.16.0/bin/start-cluster.sh

WebUI を表示: http://hadoop1:8081

(3) FlinkのSQLクライアントを起動します

/opt/module/flink-1.16.0/bin/sql-client.sh embedded

カタログを作成して使用する

文法指導

CREATE CATALOG <catalog_name> WITH (
  'type'='iceberg',
  `<config_key>`=`<config_value>`
); 
  • タイプ: 氷山でなければなりません。(しなければならない)

  • カタログ タイプ: 2 つの組み込みカタログ (hive と hadoop) があり、catalog-impl を使用してカタログをカスタマイズすることもできます。(オプション)

  • category-impl: カスタム カタログ実装の完全修飾クラス名。カタログタイプが設定されていない場合は設定する必要があります。(オプション)

  • property-version: プロパティのバージョンを説明するバージョン番号。この属性は、属性形式が変更された場合に備えて、下位互換性のために使用できます。現在のプロパティのバージョンは 1 です。(オプション)

  • cache-enabled: ディレクトリ キャッシュを有効にするかどうか。デフォルト値は true です。(オプション)

  • cache.expiration-interval-ms: ローカル キャッシュ カタログ エントリの時間 (ミリ秒)。-1 などの負の値は、時間制限がないことを意味し、0 に設定することは許可されません。デフォルト値は -1 です。(オプション)

ハイブカタログ

(1) ハイブコネクタを flink の lib にアップロードする

cp flink-sql-connector-hive-3.1.2_2.12-1.16.0.jar /opt/module/flink-1.16.0/lib/

(2) ハイムメタストアサービスの起動

hive --service metastore

(3) ハイブリットカタログの作成

flink クラスターを再起動し、sql-client を再入力します。

CREATE CATALOG hive_catalog WITH (
  'type'='iceberg',
  'catalog-type'='hive',
  'uri'='thrift://hadoop1:9083',
  'clients'='5',
  'property-version'='1',
  'warehouse'='hdfs://hadoop1:8020/warehouse/iceberg-hive'
);

use catalog hive_catalog;

カタログ hive_catalog を使用します。

  • uri: Hive メタストアの thrift uri。(必須)

  • クライアント:Hive メタストア クライアント プール サイズ、デフォルトは 2。(オプション)

  • ウェアハウス: データ ウェアハウス ディレクトリ。

  • hive-conf-dir: hive-site.xml 構成ファイルを含むディレクトリ パス。hive-site.xml 内の hive.metastore.warehouse.dir の値は、ウェアハウスによって上書きされます。

  • hadoop-conf-dir: core-site.xml および hdfs-site.xml 構成ファイルを含むディレクトリ パス。

Hadoopカタログ

Iceberg は、HDFS のディレクトリベースのカタログもサポートしています。これは、「catalog-type」=「hadoop」を使用して構成できます。

CREATE CATALOG hadoop_catalog WITH (
  'type'='iceberg',
  'catalog-type'='hadoop',
  'warehouse'='hdfs://hadoop1:8020/warehouse/iceberg-hadoop',
  'property-version'='1'
);

use catalog hadoop_catalog;
  • ウェアハウス: メタデータ ファイルとデータ ファイルが保存される HDFS ディレクトリ。(必須)

SQLクライアント初期化ファイルを構成する

vim /opt/module/flink-1.16.0/conf/sql-client-init.sql

CREATE CATALOG hive_catalog WITH (
  'type'='iceberg',
  'catalog-type'='hive',
  'uri'='thrift://hadoop1:9083',
  'warehouse'='hdfs://hadoop1:8020/warehouse/iceberg-hive'
);

USE CATALOG hive_catalog;

後で sql-client を開始する場合は、-i sql ファイル パスを追加してカタログの初期化を完了します。

/opt/module/flink-1.16.0/bin/sql-client.sh embedded -i conf/sql-client-init.sql

DDL ステートメント

データベースの作成

CREATE DATABASE iceberg_db;
USE iceberg_db;

テーブルの作成

CREATE TABLE `hive_catalog`.`default`.`sample` (
    id BIGINT COMMENT 'unique id',
    data STRING
);

テーブル作成コマンドは、次のような最も一般的に使用される flink テーブル作成構文をサポートするようになりました。

  • PARTITION BY (column1, column2, …): パーティションを設定します。Apache Flink はまだ隠しパーティションをサポートしていません。

  • COMMENT 'table document': 指定されたテーブルに関するコメント

  • WITH ('key'='value', …): テーブル属性を設定します

現在、計算列とウォーターマーク (主キーはサポートされています) はサポートされていません。

(1) パーティションテーブルの作成

CREATE TABLE `hive_catalog`.`default`.`sample` (
    id BIGINT COMMENT 'unique id',
    data STRING
) PARTITIONED BY (data);

Apache Iceberg は隠しパーティショニングをサポートしていますが、Apache flink は列の関数によるパーティショニングをサポートしていないため、flink DDL で隠しパーティショニングをサポートすることはできません。

(2) LIKE 構文を使用してテーブルを作成する

LIKE 構文は、別のテーブルと同じスキーマ、パーティション、属性を持つテーブルを作成するために使用されます。

CREATE TABLE `hive_catalog`.`default`.`sample` (
    id BIGINT COMMENT 'unique id',
    data STRING
);

CREATE TABLE  `hive_catalog`.`default`.`sample_like` LIKE `hive_catalog`.`default`.`sample`;

テーブルの変更

(1) テーブル属性の変更

ALTER TABLE `hive_catalog`.`default`.`sample` SET ('write.format.default'='avro');

(2) テーブル名を変更する

ALTER TABLE `hive_catalog`.`default`.`sample` RENAME TO `hive_catalog`.`default`.`new_sample`;

テーブルの削除

DROP TABLE `hive_catalog`.`default`.`sample`;

挿入ステートメント

に挿入

INSERT INTO `hive_catalog`.`default`.`sample` VALUES (1, 'a');
INSERT INTO `hive_catalog`.`default`.`sample` SELECT id, data from sample2;

上書き挿入

Flink のバッチ モードのみをサポートします。

SET execution.runtime-mode = batch;
INSERT OVERWRITE sample VALUES (1, 'a');
INSERT OVERWRITE `hive_catalog`.`default`.`sample` PARTITION(data='a') SELECT 6;

アップサート

Iceberg は、データを v2 テーブル形式に書き込むときに主キー ベースの UPSERT をサポートします。Upsert を有効にするには 2 つの方法があります。

(1) テーブル作成時に指定

CREATE TABLE `hive_catalog`.`test1`.`sample5` (
    `id`  INT UNIQUE COMMENT 'unique id',
    `data` STRING NOT NULL,
    PRIMARY KEY(`id`) NOT ENFORCED
) with (
    'format-version'='2', 
    'write.upsert.enabled'='true'
);

(2)挿入時に指定

INSERT INTO tableName /*+ OPTIONS('upsert-enabled'='true') */
...

挿入されたテーブルの format-version は 2 である必要があります。

OVERWRITE と UPSERT を同時に設定することはできません。UPSERT モードでは、テーブルがパーティション化されている場合、パーティション化フィールドも主キーである必要があります。

(3) Kafka ストリームを読み取り、氷山テーブルに upsert を挿入します。

create table default_catalog.default_database.kafka(
    id int,
    data string
) with (
    'connector' = 'kafka'
    ,'topic' = 'test111'
    ,'properties.zookeeper.connect' = 'hadoop1:2181'
    ,'properties.bootstrap.servers' = 'hadoop1:9092'
    ,'format' = 'json'
    ,'properties.group.id'='iceberg'
    ,'scan.startup.mode'='earliest-offset'
);


INSERT INTO hive_catalog.test1.sample5 SELECT * FROM default_catalog.default_database.kafka;

フレーズをチェックする

Iceberg は Flink のストリーミングとバッチ読み取りをサポートしています。

バッチモード

SET execution.runtime-mode = batch;
select * from sample;

ストリーミングモード

SET execution.runtime-mode = streaming;
SET table.dynamic-table-options.enabled=true;
SET sql-client.execution.result-mode=tableau;

(1) 現在のスナップショットからすべてのレコードを読み取り、そのスナップショットから増分データを読み取ります。

SELECT * FROM sample5 /*+ OPTIONS('streaming'='true', 'monitor-interval'='1s')*/ ;

(2) 指定されたスナップショット ID 以降の増分データを読み取ります (含まれていません)

SELECT * FROM sample /*+ OPTIONS('streaming'='true', 'monitor-interval'='1s', 'start-snapshot-id'='3821550127947089987')*/ ;
  • 監視間隔: 新しく送信されたデータ ファイルを継続的に監視する時間間隔 (デフォルトは 10 秒)。

  • start-snapshot-id: ストリーミング ジョブが開始されるスナップショット ID。

**注意:** アイスバーグ テーブルへの無制限のデータ ストリーミング upsert (kafka を読んで、iceberg テーブルへの upsert) の場合、ストリームでアイスバーグ テーブルを読み取るときにデータを読み取ることができないという問題が発生します。 。無制限のデータ ストリームが氷山テーブルに追加された場合 (kafka を読み、氷山テーブルに追加)、結果は通常、氷山テーブルをストリーミングすることで確認できます。

Flink と統合するデメリット

サポートされている機能 かなり 述べる
SQLでカタログを作成
SQLでデータベースを作成する
SQLでテーブルを作成する
SQLは次のようなテーブルを作成します
SQL変更テーブル テーブル属性の変更のみがサポートされており、列とパーティションの変更はサポートされていません。
SQL ドロップテーブル
SQL選択 ストリーミングおよびバッチ処理モードをサポート
SQL 挿入 ストリーミングおよびバッチ処理モードをサポート
SQL挿入上書き
データストリームの読み取り
データストリームの追加
データストリームの上書き
メタデータテーブル Java APIをサポートしますが、Flink SQLはサポートしません
Rewrite files action
  • 不支持创建隐藏分区的Iceberg表。

  • 不支持创建带有计算列的Iceberg表。

  • 不支持创建带watermark的Iceberg表。

  • 不支持添加列,删除列,重命名列,更改列。

  • Iceberg目前不支持Flink SQL 查询表的元数据信息,需要使用Java API 实现。

与 Flink DataStream 集成

环境准备

(1)配置pom文件

新建Maven工程,pom文件配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.atguigu.iceberg</groupId>
    <artifactId>flink-iceberg-demo</artifactId>
    <version>1.0-SNAPSHOT</version>


    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <flink.version>1.16.0</flink.version>
        <java.version>1.8</java.version>
        <scala.binary.version>2.12</scala.binary.version>
        <slf4j.version>1.7.30</slf4j.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-java</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>   <!--不会打包到依赖中,只参与编译,不参与运行 -->
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-java</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-clients</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.flink/flink-table-planner -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-planner_${scala.binary.version}</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-files</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>
        </dependency>

        <!--idea运行时也有webui-->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-runtime-web</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.14.0</version>
            <scope>provided</scope>
        </dependency>


        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-statebackend-rocksdb</artifactId>
            <version>${flink.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>3.1.3</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.iceberg/iceberg-flink-runtime-1.16 -->
        <dependency>
            <groupId>org.apache.iceberg</groupId>
            <artifactId>iceberg-flink-runtime-1.16</artifactId>
            <version>1.1.0</version>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <excludes>
                                    <exclude>com.google.code.findbugs:jsr305</exclude>
                                    <exclude>org.slf4j:*</exclude>
                                    <exclude>log4j:*</exclude>
                                    <exclude>org.apache.hadoop:*</exclude>
                                </excludes>
                            </artifactSet>
                            <filters>
                                <filter>
                                    <!-- Do not copy the signatures in the META-INF folder.
                                    Otherwise, this might cause SecurityExceptions when using the JAR. -->
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <transformers combine.children="append">
                                <transformer
                                             implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer">
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

(2)配置log4j

resources目录下新建log4j.properties。

log4j.rootLogger=error,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

读取数据

常规Source写法

(1)Batch方式

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
TableLoader tableLoader = TableLoader.fromHadoopTable("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a");
DataStream<RowData> batch = FlinkSource.forRowData()
     .env(env)
     .tableLoader(tableLoader)
     .streaming(false)
     .build();

batch.map(r -> Tuple2.of(r.getLong(0),r.getLong(1) ))
      .returns(Types.TUPLE(Types.LONG,Types.LONG))
      .print();

env.execute("Test Iceberg Read");

(2)Streaming方式

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
TableLoader tableLoader = TableLoader.fromHadoopTable("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a"); 
DataStream<RowData> stream = FlinkSource.forRowData()
    .env(env)
    .tableLoader(tableLoader)
    .streaming(true)
    .startSnapshotId(3821550127947089987L)
    .build();

stream.map(r -> Tuple2.of(r.getLong(0),r.getLong(1) ))
    .returns(Types.TUPLE(Types.LONG,Types.LONG))
    .print();

env.execute("Test Iceberg Read");

FLIP-27 Source写法

(1)Batch方式

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
TableLoader tableLoader = TableLoader.fromHadoopTable("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a");

IcebergSource<RowData> source1 = IcebergSource.forRowData()
    .tableLoader(tableLoader)
    .assignerFactory(new SimpleSplitAssignerFactory())
    .build();

DataStream<RowData> batch = env.fromSource(
    Source1,
    WatermarkStrategy.noWatermarks(),
    "My Iceberg Source",
    TypeInformation.of(RowData.class));

batch.map(r -> Tuple2.of(r.getLong(0), r.getLong(1)))
    .returns(Types.TUPLE(Types.LONG, Types.LONG))
    .print();

env.execute("Test Iceberg Read");

(2)Streaming方式

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
TableLoader tableLoader = TableLoader.fromHadoopTable("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a");

IcebergSource source2 = IcebergSource.forRowData()
    .tableLoader(tableLoader)
    .assignerFactory(new SimpleSplitAssignerFactory())
    .streaming(true)
    .streamingStartingStrategy(StreamingStartingStrategy.INCREMENTAL_FROM_LATEST_SNAPSHOT)
    .monitorInterval(Duration.ofSeconds(60))
    .build();

DataStream<RowData> stream = env.fromSource(
    Source2,
    WatermarkStrategy.noWatermarks(),
    "My Iceberg Source",
    TypeInformation.of(RowData.class));

stream.map(r -> Tuple2.of(r.getLong(0), r.getLong(1)))
    .returns(Types.TUPLE(Types.LONG, Types.LONG))
    .print();

env.execute("Test Iceberg Read");

写入数据

目前支持DataStream<RowData>和DataStream<Row>格式的数据流写入Iceberg表。

(1)写入方式支持 append、overwrite、upsert

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);


SingleOutputStreamOperator<RowData> input = env.fromElements("")
    .map(new MapFunction<String, RowData>() {
    
    
        @Override
        public RowData map(String s) throws Exception {
    
    
            GenericRowData genericRowData = new GenericRowData(2);
            genericRowData.setField(0, 99L);
            genericRowData.setField(1, 99L);

            return genericRowData;
        }
    });

TableLoader tableLoader = TableLoader.fromHadoopTable("hdfs://hadoop1:8020/warehouse/spark-iceberg/default/a");


FlinkSink.forRowData(input)
    .tableLoader(tableLoader)
    .append()            // append方式
    //.overwrite(true)   // overwrite方式
    //.upsert(true)       // upsert方式
    ;

env.execute("Test Iceberg DataStream");

(2)写入选项

FlinkSink.forRowData(input)
    .tableLoader(tableLoader)
    .set("write-format", "orc")
    .set(FlinkWriteOptions.OVERWRITE_MODE, "true");

可配置选项如下:

选项 默认值 说明
write-format Parquet同write.format.default 写入操作使用的文件格式:Parquet, avro或orc
target-file-size-bytes 536870912(512MB)同write.target-file-size-bytes 控制生成的文件的大小,目标大约为这么多字节
upsert-enabled 同write.upsert.enabled,
overwrite-enabled false 覆盖表的数据,不能和UPSERT模式同时开启
distribution-mode None同 write.distribution-mode 定义写数据的分布方式: none:不打乱行; hash:按分区键散列分布;range:如果表有SortOrder,则通过分区键或排序键分配
compression-codec 同 write.(fileformat).compression-codec
compression-level 同様に書き込みます。(ファイル形式).圧縮レベル
圧縮戦略 同じくwrite.orc.compression-strategy

小さなファイルを結合する

Iceberg は現在、flink SQL でのテーブルのチェックをサポートしていません。Iceberg が提供する Java API を使用してメタデータを読み取り、テーブル情報を取得する必要があります。Flink バッチ ジョブを送信すると、小さなファイルを大きなファイルに書き換えることができます。

import org.apache.iceberg.flink.actions.Actions;

// 1.获取 Table对象
// 1.1 创建 catalog对象
Configuration conf = new Configuration();
HadoopCatalog hadoopCatalog = new HadoopCatalog(conf, "hdfs://hadoop1:8020/warehouse/spark-iceberg");

// 1.2 通过 catalog加载 Table对象
Table table = hadoopCatalog.loadTable(TableIdentifier.of("default", "a"));

// 有Table对象,就可以获取元数据、进行维护表的操作
//        System.out.println(table.history());
//        System.out.println(table.expireSnapshots().expireOlderThan());

// 2.通过 Actions 来操作 合并
Actions.forTable(table)
    .rewriteDataFiles()
    .targetSizeInBytes(1024L)
    .execute();

Table オブジェクトを取得した後、メタデータを取得し、テーブルのメンテナンス操作を実行できます。Iceberg が提供するその他の API 操作については、https: //iceberg.apache.org/docs/latest/api/を確認してください。

おすすめ

転載: blog.csdn.net/qq_44766883/article/details/131488124