데이터 레이크 Iceberg 도입 및 활용 (Hive, SparkSQL, FlinkSQL 통합)

기사 디렉토리

소개

개요

데이터 스토리지와 컴퓨팅 엔진 간의 적응 문제를 해결하기 위해 넷플릭스는 아이스버그(Iceberg)를 개발했으며, 2018년 11월 16일 아파치 인큐베이터에 진입하고 2020년 5월 19일 인큐베이터를 졸업해 아파치의 최상위 프로젝트가 됐다.

Iceberg는 대규모 데이터 분석 시나리오를 위한 오픈 테이블 형식(Table Format) 입니다 . 테이블 형식 은 컴퓨팅 프레임워크(Flink, Spark...) 및 데이터 파일 위에 메타데이터 및 데이터 파일을 구성하는 방법으로 이해될 수 있습니다 .

효과

빅데이터 분야는 오랜 기간 개발과 탐구를 거쳐 왔으며, 빅데이터 기술의 출현과 반복으로 인해 사용자가 대용량 데이터를 처리할 수 있는 문턱이 낮아지고 있지만, 데이터 형식의 적응이라는 무시할 수 없는 문제가 있습니다. 다른 엔진에.

즉, 계산을 위해 다른 엔진을 사용할 때 엔진에 따라 데이터를 조정해야 합니다. 이것은 꽤 까다로운 질문입니다.

이를 위해 상위 컴퓨팅 엔진과 기본 스토리지 형식 사이의 중간 계층이라는 새로운 솔루션이 등장했습니다. 이 중간 계층은 데이터 저장 방법이 아니라 데이터의 메타데이터 구성 방법만 정의하고, 기존 데이터베이스의 "테이블"과 유사한 통일된 의미를 엔진 수준에 제공합니다. 기본 레이어는 여전히 Parquet 및 ORC와 같은 저장 형식입니다. 이를 바탕으로 넷플릭스는 현재 아파치의 최상위 프로젝트인 아이스버그(Iceberg)를 개발했다.

특성

데이터 저장 및 컴퓨팅 엔진 플러그인

Iceberg는 특정 데이터 저장소나 계산 엔진에 얽매이지 않는 개방형 범용 테이블 형식 구현 솔루션을 제공합니다. 현재 빅데이터 분야의 공용 데이터 스토리지(HDFS, S3...)와 컴퓨팅 엔진(Flink, Spark...)을 아이스버그에 연결할 수 있다.

프로덕션 환경에서는 다양한 구성요소를 선택하여 함께 사용할 수 있습니다. 계산 엔진을 거치지 않고 파일 시스템에 저장된 데이터를 직접 읽을 수도 있습니다.

실시간 스트리밍 및 일괄 통합

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는 기존 테이블을 직접 수정할 수 있습니다.

테이블의 파티셔닝 전략을 변경하면 수정된 파티션 이전의 데이터는 변경되지 않습니다. 이전 파티셔닝 전략은 계속 사용되고 새 데이터는 새로운 파티셔닝 전략을 사용합니다. 즉, 동일한 테이블은 두 가지 분할 전략. , 이전 데이터는 이전 분할 전략을 채택하고 새 데이터는 새로운 분할 전략을 채택합니다. 메타데이터에서 두 분할 전략은 서로 독립적이며 겹치지 않습니다.

데이터를 쿼리할 때 파티션 간 전략이 있는 경우 Iceberg 공식 웹사이트에서 제공한 그림과 같이 두 가지 실행 계획으로 구문 분석됩니다.

MM크기

그림의 booking_table 테이블은 2008년에 월별로 파티션을 나누었다가 2009년에 들어와서 일별로 파티션을 나누는 방식으로 변경되었습니다. 이러한 중간 파티셔닝 전략이 테이블에 공존하고 있습니다.

Iceberg의 Hidden Partition을 사용하면 SQL 쿼리를 작성할 때 SQL에 파티션 필터 조건을 지정할 필요가 없으며 Iceberg는 자동으로 불필요한 데이터를 분할하고 필터링합니다.

Iceberg 파티션 진화 작업은 메타데이터 작업이기도 하며 데이터 파일을 다시 쓰지 않습니다.

정렬 순서 진화

Iceberg는 기존 테이블의 정렬 전략을 수정할 수 있습니다. 정렬 전략을 수정한 후에도 이전 데이터는 여전히 이전 정렬 전략을 채택합니다. Iceberg에 데이터를 쓰는 계산 엔진은 항상 최신 정렬 전략을 선택하지만 정렬 비용이 극도로 높을 경우 정렬이 수행되지 않습니다.

숨겨진 파티션

Iceberg의 파티션 정보는 수동 유지 관리가 필요하지 않으며 숨길 수 있습니다. Hive와 유사한 다른 파티션 전략과 달리 Iceberg의 파티션 필드/전략(특정 필드에서 계산)은 테이블의 필드 또는 테이블 데이터 저장 디렉터리일 필요가 없습니다. .. 상관없습니다. 테이블을 생성하거나 파티셔닝 전략을 수정한 후 새 데이터는 해당 데이터가 속한 파티션을 자동으로 계산합니다. 쿼리할 때 관계형 테이블을 분할하는 데 어떤 필드/전략이 사용되는지 알 필요도 없고 비즈니스 로직에만 집중하면 됩니다. Iceberg는 불필요한 분할 데이터를 자동으로 필터링합니다.

아이스버그의 파티션 정보와 테이블 데이터 저장 디렉터리가 독립적이기 때문에 아이스버그의 테이블 파티션을 수정할 수 있고, 불일치에는 데이터 마이그레이션이 포함된다.

미러 데이터 쿼리(시간 여행)

Iceberg는 쿼리 테이블 기록의 특정 지점에서 데이터 미러링(스냅샷)을 쿼리하는 기능을 제공합니다. 이 기능을 통해 과거 데이터에 최신 SQL 로직을 적용할 수 있습니다.

지원 거래(ACID)

트랜잭션(ACID) 메커니즘을 제공함으로써 Iceberg는 upsert 기능을 갖추고 있으며 쓰는 동안 읽을 수 있도록 하여 다운스트림 구성 요소에서 데이터를 더 빠르게 사용할 수 있도록 합니다. 트랜잭션은 다운스트림 구성 요소가 커밋된 데이터만 사용할 수 있고 일부 또는 커밋되지 않은 데이터도 읽지 않도록 보장합니다.

낙관적 잠금 기반 동시성 지원

낙관적 잠금을 기반으로 Iceberg는 여러 프로그램이 동시에 쓸 수 있는 기능을 제공하고 데이터의 선형 일관성을 보장합니다.

파일 수준 데이터 정리

Iceberg의 메타데이터는 최대값, 최소값, 개수 개수 등과 같은 각 데이터 파일에 대한 일부 통계 정보를 제공합니다. 따라서 기존의 파티셔닝 및 컬럼 필터링 외에도 SQL 쿼리를 위한 필터링 조건을 파일 수준까지 푸시할 수 있어 쿼리 효율성이 크게 향상됩니다.

다른 데이터 레이크 프레임워크 비교

MM크기

MM크기

저장 구조

MM크기

MM크기

데이터 파일데이터 파일

데이터 파일은 Apache Iceberg 테이블이 실제로 데이터를 저장하는 파일입니다. 일반적으로 테이블의 데이터 저장 디렉터리 중 데이터 디렉터리에 있습니다. 파일 형식이 parquet인 경우 파일은 ".parquet"로 끝납니다.

예: 00000-0-atguigu_20230203160458_22ee74c9-643f-4b27-8fc1-9cbd5f64dad4-job_1675409881387_0007-00001.parquet는 데이터 파일입니다.

Iceberg는 각 업데이트에 대해 여러 데이터 파일을 생성합니다.

테이블 스냅샷스냅샷

스냅샷은 특정 시점의 테이블 상태를 나타냅니다. 각 스냅샷에는 특정 시간에 테이블의 모든 데이터 파일 목록이 포함되어 있습니다. 데이터 파일은 다른 매니페스트 파일에 저장되고 매니페스트 파일은 매니페스트 목록 파일에 저장되며 매니페스트 목록 파일은 스냅샷을 나타냅니다.

매니페스트 목록 매니페스트 목록

매니페스트 목록은 테이블 스냅샷을 작성하기 위한 매니페스트 파일을 나열하는 메타데이터 파일입니다. 이 메타데이터 파일에는 매니페스트 파일 목록이 저장되어 있으며 각 매니페스트 파일은 한 줄을 차지합니다. 각 줄에는 매니페스트 파일의 경로, 저장하는 데이터 파일의 파티션 범위, 추가된 데이터 파일 수, 삭제된 데이터 파일 수 등의 정보가 저장되며, 이 정보는 쿼리 중 필터링을 제공하는 데 사용될 수 있습니다. .부스트.

예: snap-6746266566064388720-1-52f2f477-2585-4e69-be42-bbad9a46ed17.avro는 매니페스트 목록 파일입니다.

매니페스트 파일

매니페스트 파일은 스냅샷을 구성하는 데이터 파일의 목록 정보를 나열하는 메타데이터 파일이기도 합니다. 각 줄에는 데이터 파일의 상태, 파일 경로, 파티션 정보, 열 수준의 통계 정보(예: 각 열의 최대값과 최소값, null 값의 개수, 등), 파일 크기, 파일 내용 데이터 행 수 등의 정보. 열 수준 통계는 테이블 데이터를 스캔할 때 불필요한 파일을 필터링할 수 있습니다.

매니페스트 파일은 ".avro" 접미사로 끝나는 avro 형식으로 저장됩니다(예: 52f2f477-2585-4e69-be42-bbad9a46ed17-m0.avro).

Hive와 통합

환경 준비

(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) 하둡 시작

카탈로그 생성 및 관리

Iceberg는 Hive, Hadoop, Amazon의 AWS Glue 및 사용자 지정 카탈로그와 같은 다양한 카탈로그 유형을 지원합니다.

다양한 구성에 따라 세 가지 상황이 있습니다.

  • iceberg.catalog가 설정되지 않고 기본적으로 HiveCatalog가 사용됩니다.
구성 항목 설명하다
iceberg.catalog.<카탈로그_이름>.type 카탈로그 유형: hive, hadoop, 사용자 정의 카탈로그를 사용하는 경우 설정되지 않음
iceberg.catalog.<카탈로그_이름>.catalog-impl 카탈로그 구현 클래스, 위 유형이 설정되지 않은 경우 이 매개변수를 설정해야 합니다.
iceberg.catalog.<카탈로그_이름>.<키> 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);

로드할 경로 지정

HDFS에 iceberg 형식 테이블이 이미 존재하는 경우 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(timestamp)). 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) 스파크 설치

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의 jars 디렉터리에 복사합니다.

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

(2) 하둡 시작

Spark 구성 카탈로그

Spark는 hive와 hadoop의 두 가지 카탈로그 설정을 지원합니다. 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;

하둡 카탈로그

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(파티션 표현식): 파티션 구성

  • 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 분할과 동일

  • hour(ts) 또는 date_hour(ts): dateint 및 hour 분할과 동일합니다.

  • bucket(N, col): mod N 버킷을 해시 값으로 나눕니다.

  • truncate(L, col): L로 잘린 값으로 나눕니다.

문자열이 지정된 길이로 잘립니다.

정수 및 긴 유형은 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 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 및 소수 필드 유형 확장

  • 필수 열을 선택사항으로 설정

또한 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 Catalog에서 프로시저를 사용할 수 있습니다. 모든 프로시저는 네임스페이스에 있습니다.

(1) 문법

매개변수 이름에 따라 매개변수 전달

CALL catalog_name.system.procedure_name(arg_name_2 => arg_2, arg_name_1 => arg_1)

위치별로 인수를 전달할 때 end 인수가 선택적이면 end 인수만 생략할 수 있습니다.

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

DataFrame 작업

환경 준비

(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) 테이블 객체 가져오기

1) 하둡 카탈로그

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) 플링크 설치

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) 하둡 시작

(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

웹UI 보기: http://hadoop1:8081

(3) Flink의 sql-client 시작

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

카탈로그 생성 및 사용

문법 지침

CREATE CATALOG <catalog_name> WITH (
  'type'='iceberg',
  `<config_key>`=`<config_value>`
); 
  • 유형: 빙산이어야 합니다. (해야 하다)

  • Catalog-type: hive와 hadoop이라는 두 가지 내장 카탈로그가 있으며, Catalog-impl을 사용하여 카탈로그를 사용자 정의할 수도 있습니다. (선택 과목)

  • Catalog-impl: 사용자 정의 카탈로그 구현의 정규화된 클래스 이름입니다. 카탈로그 유형이 설정되지 않은 경우 설정해야 합니다. (선택 과목)

  • property-version: 속성 버전을 설명하는 버전 번호입니다. 이 속성은 속성 형식이 변경되는 경우 이전 버전과의 호환성을 위해 사용할 수 있습니다. 현재 속성 버전은 1입니다. (선택 과목)

  • 캐시 활성화: 디렉터리 캐싱 활성화 여부, 기본값은 true입니다. (선택 과목)

  • 캐시.만료-간격-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 메타스토어의 절약형 uri입니다. (필수의)

  • 클라이언트: Hive Metastore 클라이언트 풀 크기, 기본값은 2입니다. (선택 과목)

  • 창고: 데이터 창고 디렉토리.

  • hive-conf-dir: hive-site.xml 구성 파일이 포함된 디렉터리 경로입니다. hive-site.xml의 hive.metastore.warehouse.dir 값은 웨어하우스에서 덮어쓰게 됩니다.

  • hadoop-conf-dir: core-site.xml 및 hdfs-site.xml 구성 파일이 포함된 디렉터리의 경로입니다.

하둡 카탈로그

Iceberg는 'catalog-type'='hadoop'을 사용하여 구성할 수 있는 HDFS의 디렉터리 기반 카탈로그도 지원합니다.

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

use catalog hadoop_catalog;
  • Warehouse: 메타데이터 파일과 데이터 파일이 저장되는 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;

UPSERT

Iceberg는 v2 테이블 형식에 데이터를 쓸 때 기본 키를 기반으로 하는 UPSERT를 지원합니다. upsert를 활성화하는 방법에는 두 가지가 있습니다.

(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')*/ ;
  • monitor-interval: 새로 제출된 데이터 파일을 지속적으로 모니터링하는 시간 간격(기본값은 10초).

  • start-snapshot-id: 스트리밍 작업이 시작되는 스냅샷 ID입니다.

**참고:** 빙산 테이블에 무제한 데이터 스트리밍 upsert(kafka 읽기, 빙산 테이블에 upsert)인 경우 스트림에서 빙산 테이블을 읽을 때 데이터를 읽을 수 없는 문제가 있습니다. . 빙산표에 무한한 데이터를 스트리밍 방식(kafka 읽기, 빙산표에 추가)으로 추가하면 빙산표를 읽을 때 정상적으로 결과를 볼 수 있다.

Flink와의 통합의 단점

지원되는 기능 많은 주목
SQL 생성 카탈로그
SQL 데이터베이스 생성
SQL 생성 테이블
SQL은 다음과 같은 테이블을 생성합니다.
SQL 변경 테이블 테이블 속성 수정만 지원되며 열 및 파티션 변경은 지원되지 않습니다.
SQL drop_table
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-전략

작은 파일 병합

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

테이블 개체를 얻으면 메타데이터를 얻을 수 있고 테이블 유지 관리 작업을 수행할 수 있습니다. Iceberg에서 제공하는 더 많은 API 작업을 보려면 https://iceberg.apache.org/docs/latest/api/ 를 확인하세요.

추천

출처blog.csdn.net/qq_44766883/article/details/131488124