

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 읽기 성능 최적화
<a name="best-practices-read"></a>

이 섹션에서는 엔진과 관계없이 읽기 성능을 최적화하기 위해 조정할 수 있는 테이블 속성에 대해 설명합니다.

## 분할
<a name="read-partitioning"></a>

Hive 테이블과 마찬가지로 Iceberg는 파티션을 인덱싱의 기본 계층으로 사용하여 불필요한 메타데이터 파일과 데이터 파일을 읽지 않도록 합니다. 또한 쿼리 계획을 더욱 개선하기 위해 열 통계를 인덱싱의 보조 계층으로 고려하여 전체 실행 시간을 개선합니다.

### 데이터 파티셔닝
<a name="read-partitioning-data"></a>

Iceberg 테이블을 쿼리할 때 스캔되는 데이터의 양을 줄이려면 예상 읽기 패턴과 일치하는 균형 잡힌 파티션 전략을 선택합니다.
+ 쿼리에 자주 사용되는 열을 식별합니다. 이들은 이상적인 파티셔닝 후보입니다. 예를 들어 일반적으로 특정 날짜의 데이터를 쿼리하는 경우 파티션 열의 자연스러운 예는 날짜 열입니다.
+ 파티션 수가 너무 많지 않도록 하려면 카디널리티가 낮은 파티션 열을 선택합니다. 파티션이 너무 많으면 테이블의 파일 수가 증가하여 쿼리 성능에 부정적인 영향을 미칠 수 있습니다. 일반적으로 "파티션이 너무 많음"은 대다수 파티션의 데이터 크기가에서 설정한 값의 2\$15배 미만인 시나리오로 정의할 수 있습니다`target-file-size-bytes`.

**참고**  
일반적으로 카디널리티가 높은 열(예: 수천 개의 값을 가질 수 있는 `id` 열)에서 필터를 사용하여 쿼리하는 경우 다음 섹션에 설명된 대로 버킷 변환과 함께 Iceberg의 숨겨진 파티셔닝 기능을 사용합니다.

### 숨겨진 파티셔닝 사용
<a name="read-partitioning-hidden"></a>

쿼리가 일반적으로 테이블 열의 파생물을 기준으로 필터링하는 경우 파티션으로 사용할 새 열을 명시적으로 생성하는 대신 숨겨진 파티션을 사용합니다. 이 기능에 대한 자세한 내용은 [Iceberg 설명서를](https://iceberg.apache.org/docs/latest/partitioning/#icebergs-hidden-partitioning) 참조하세요.

예를 들어 타임스탬프 열이 있는 데이터 세트(예: `2023-01-01 09:00:00`)에서는 구문 분석된 날짜(예: `2023-01-01`)로 새 열을 생성하는 대신 파티션 변환을 사용하여 타임스탬프에서 날짜 부분을 추출하고 즉시 이러한 파티션을 생성합니다.

숨겨진 파티셔닝의 가장 일반적인 사용 사례는 다음과 같습니다.
+ 데이터에 타임스탬프 열이 있는 경우 **날짜 또는 시간에 대한 파티셔닝**. Iceberg는 타임스탬프의 날짜 또는 시간 부분을 추출하기 위해 여러 변환을 제공합니다.
+ **분할 열의 카디널리티가 높아 파티션이 너무 많은 경우 열의 해시 함수**에서 분할합니다. Iceberg의 버킷 변환은 파티셔닝 열에서 해시 함수를 사용하여 여러 파티션 값을 더 적은 수의 숨겨진(버킷) 파티션으로 그룹화합니다.

사용 가능한 모든 [파티션 변환](https://iceberg.apache.org/spec/#partition-transforms)에 대한 개요는 Iceberg 설명서의 파티션 변환을 참조하세요.

숨겨진 파티셔닝에 사용되는 열은 `year()` 및와 같은 일반 SQL 함수를 사용하여 쿼리 조건자의 일부가 될 수 있습니다`month()`. 조건자는 `BETWEEN` 및와 같은 연산자와 결합할 수도 있습니다`AND`.

**참고**  
Iceberg는와 같이 다른 데이터 유형을 생성하는 함수에 대해 파티션 정리를 수행할 수 없습니다`substring(event_time, 1, 10) = '2022-01-01'`.

### 파티션 진화 사용
<a name="read-partitioning-evolution"></a>

기존 [파티션 전략이 최적이 아닌 경우 Iceberg의 파티션 진화](https://iceberg.apache.org/docs/latest/evolution/#partition-evolution)를 사용합니다. 예를 들어 너무 작은(각각 몇 메가바이트) 시간당 파티션을 선택하는 경우 일별 또는 월별 파티션으로 전환하는 것이 좋습니다.

이 접근 방식은 테이블에 대한 최상의 파티션 전략이 처음에는 명확하지 않고 더 많은 인사이트를 얻으면서 파티셔닝 전략을 구체화하려는 경우에 사용할 수 있습니다. 파티션 진화의 또 다른 효과적인 사용은 데이터 볼륨이 변경되고 현재 파티셔닝 전략이 시간이 지남에 따라 덜 효과적이 되는 경우입니다.

파티션을 발전시키는 방법에 대한 지침은 Iceberg 설명서의 [ALTER TABLE SQL 확장](https://iceberg.apache.org/docs/latest/spark-ddl/#alter-table-sql-extensions)을 참조하세요. 

## 파일 크기 조정
<a name="read-file-size"></a>

쿼리 성능을 최적화하려면 테이블의 작은 파일 수를 최소화해야 합니다. 쿼리 성능을 높이려면 일반적으로 Parquet 및 ORC 파일을 100MB보다 크게 유지하는 것이 좋습니다.

파일 크기는 Iceberg 테이블의 쿼리 계획에도 영향을 미칩니다. 테이블의 파일 수가 증가하면 메타데이터 파일의 크기도 증가합니다. 메타데이터 파일이 클수록 쿼리 계획이 느려질 수 있습니다. 따라서 테이블 크기가 커지면 파일 크기를** 늘려 메타데이터의 지수 확장을 완화** **합니다.

다음 모범 사례를 사용하여 Iceberg 테이블에 적절한 크기의 파일을 생성합니다.

### 대상 파일 및 행 그룹 크기 설정
<a name="read-file-size-target"></a>

Iceberg는 데이터 파일 레이아웃을 튜닝하기 위해 다음과 같은 주요 구성 파라미터를 제공합니다. 이러한 파라미터를 사용하여 대상 파일 크기와 행 그룹 또는 스트라이크 크기를 설정하는 것이 좋습니다.


| **파라미터** | **기본값** | **Comment** | 
| --- |--- |--- |
| `write.target-file-size-bytes` | 512MB | 이 파라미터는 Iceberg가 생성할 최대 파일 크기를 지정합니다. 그러나 특정 파일은이 제한보다 작은 크기로 작성될 수 있습니다. | 
| `write.parquet.row-group-size-bytes` | 128MB | Parquet과 ORC 모두 데이터를 청크로 저장하므로 엔진이 일부 작업에 대해 전체 파일을 읽지 않아도 됩니다. | 
| `write.orc.stripe-size-bytes` | 64MB | 
| `write.distribution-mode` | 없음, Iceberg 버전 1.1 이하의 경우해시, Iceberg 버전 1.2부터 | Iceberg는 스토리지에 쓰기 전에 작업 간에 데이터를 정렬하도록 Spark에 요청합니다. | 
+ 예상 테이블 크기에 따라 다음 일반 지침을 따릅니다.
  + **작은 테이블**(최대 몇 기가바이트) - 대상 파일 크기를 128MB로 줄입니다. 또한 행 그룹 또는 스트라이프 크기를 줄입니다(예: 8MB 또는 16MB로).
  + **중간에서 큰 테이블**(몇 기가바이트에서 수백 기가바이트) - 기본값은 이러한 테이블의 좋은 시작점입니다. 쿼리가 매우 선택적인 경우 행 그룹 또는 스트라이프 크기를 조정합니다(예: 16MB).
  + **매우 큰 테이블**(수백 기가바이트 또는 테라바이트) - 대상 파일 크기를 1,024MB 이상으로 늘리고 쿼리가 일반적으로 대용량 데이터 세트를 가져오는 경우 행 그룹 또는 스트라이프 크기를 늘리는 것이 좋습니다.
+ Iceberg 테이블에 쓰는 Spark 애플리케이션이 적절한 크기의 파일을 생성하도록 하려면 `write.distribution-mode` 속성을 `hash` 또는 로 설정합니다`range`. 이러한 모드 간의 차이점에 대한 자세한 설명은 Iceberg 설명서의 [배포 모드 작성](https://iceberg.apache.org/docs/latest/spark-writes/#writing-distribution-modes)을 참조하세요.

다음은 일반적인 지침입니다. 테스트를 실행하여 특정 테이블 및 워크로드에 가장 적합한 값을 식별하는 것이 좋습니다.

### 정기적인 압축 실행
<a name="read-file-size-compaction"></a>

이전 테이블의 구성은 쓰기 작업에서 생성할 수 있는 최대 파일 크기를 설정하지만 파일의 크기가 해당 크기인지 보장하지는 않습니다. 적절한 파일 크기를 보장하려면 정기적으로 압축을 실행하여 작은 파일을 더 큰 파일로 결합합니다. 압축 실행에 대한 자세한 지침은이 가이드 뒷부분의 [Iceberg 압축을](best-practices-compaction.md) 참조하세요.

## 열 통계 최적화
<a name="read-column-statistics"></a>

Iceberg는 열 통계를 사용하여 파일 정리를 수행하므로 쿼리에서 스캔하는 데이터의 양을 줄여 쿼리 성능이 향상됩니다. 열 통계를 활용하려면 Iceberg가 쿼리 필터에 자주 사용되는 모든 열에 대한 통계를 수집해야 합니다.

기본적으로 Iceberg는 [테이블 속성에 정의된 대로 각 테이블의 처음 100개 열에](https://github.com/apache/iceberg/blob/ae15c7e36973501b40443e75816d3eac39eddc90/core/src/main/java/org/apache/iceberg/TableProperties.java#L276) 대한 통계만 수집합니다`write.metadata.metrics.max-inferred-column-defaults`. 테이블에 100개 이상의 열이 있고 쿼리가 처음 100개 열 외부의 열을 자주 참조하는 경우(예: 열 132를 필터링하는  쿼리가 있을 수 있음) Iceberg가 해당 열에 대한 통계를 수집하는지 확인합니다. 이를 달성하기 위한 두 가지 옵션이 있습니다.
+ Iceberg 테이블을 생성할 때 쿼리에 필요한 열이에서 설정한 열 범위`write.metadata.metrics.max-inferred-column-defaults`(기본값은 100) 내에 있도록 열을 재정렬합니다.

  참고: 100개 열에 대한 통계가 필요하지 않은 경우 `write.metadata.metrics.max-inferred-column-defaults` 구성을 원하는 값(예: 20개)으로 조정하고 쿼리를 읽고 쓰는 데 필요한 열이 데이터 세트 왼쪽의 처음 20개 열에 속하도록 열을 재정렬할 수 있습니다.
+ 쿼리 필터에 몇 개의 열만 사용하는 경우 다음 예제와 같이 지표 수집에 대한 전체 속성을 비활성화하고 통계를 수집할 개별 열을 선택적으로 선택할 수 있습니다.

  ```
  .tableProperty("write.metadata.metrics.default", "none")
  .tableProperty("write.metadata.metrics.column.my_col_a", "full")
  .tableProperty("write.metadata.metrics.column.my_col_b", "full")
  ```

참고: 열 통계는 해당 열에서 데이터를 정렬할 때 가장 효과적입니다. 자세한 내용은이 가이드 뒷부분[의 정렬 순서 설정](#read-sort-order) 섹션을 참조하세요.

## 올바른 업데이트 전략 선택
<a name="read-update"></a>

느린 copy-on-write 전략을 사용하여 읽기 성능을 최적화합니다. Iceberg에서 사용하는 기본 전략입니다.

파일이 읽기 최적화 방식으로 스토리지에 직접 기록되므로 Copy-on-write를 통해 읽기 성능이 향상됩니다. 그러나 merge-on-read에 비해 각 쓰기 작업은 더 오래 걸리고 더 많은 컴퓨팅 리소스를 소비합니다. 이는 읽기 및 쓰기 지연 시간 간의 클래식 절충점을 제공합니다. 일반적으로 copy-on-write는 대부분의 업데이트가 동일한 테이블 파티션에 공동 배치되는 사용 사례에 적합합니다(예: 일일 배치 로드의 경우).

Copy-on-write 구성(`write.update.mode`, `write.delete.mode`및 `write.merge.mode`)은 테이블 수준에서 설정하거나 애플리케이션 측에서 독립적으로 설정할 수 있습니다.

## ZSTD 압축 사용
<a name="read-compression"></a>

테이블 속성를 사용하여 Iceberg에서 사용하는 압축 코덱을 수정할 수 있습니다`write.<file_type>.compression-codec`. ZSTD 압축 코덱을 사용하여 테이블의 전반적인 성능을 개선하는 것이 좋습니다.

기본적으로 Iceberg 버전 1.3 이하에서는 ZSTD에 비해 읽기/쓰기 성능이 느린 GZIP 압축을 사용합니다.

참고: 일부 엔진은 다른 기본값을 사용할 수 있습니다. 이는 [Athena 또는 Amazon EMR 버전 7.x로 생성된 Iceberg 테이블](https://docs.aws.amazon.com/athena/latest/ug/compression-support-iceberg.html)의 경우입니다.

## 정렬 순서 설정
<a name="read-sort-order"></a>

Iceberg 테이블의 읽기 성능을 개선하려면 쿼리 필터에 자주 사용되는 하나 이상의 열을 기준으로 테이블을 정렬하는 것이 좋습니다. Iceberg의 열 통계와 함께 정렬하면 파일 정리가 훨씬 더 효율적이 되어 읽기 작업이 빨라질 수 있습니다. 또한 정렬은 쿼리 필터에서 정렬 열을 사용하는 쿼리에 대한 Amazon S3 요청 수를 줄입니다.

Spark에서 데이터 정의 언어(DDL) 문을 실행하여 테이블 수준에서 계층적 정렬 순서를 설정할 수 있습니다. 사용 가능한 옵션은 [Iceberg 설명서를](https://iceberg.apache.org/docs/latest/spark-ddl/#alter-table--write-ordered-by) 참조하세요. 정렬 순서를 설정한 후 라이터는 Iceberg 테이블의 후속 데이터 쓰기 작업에이 정렬을 적용합니다.

예를 들어 대부분의 쿼리가를 기준으로 필터링하는 날짜(`yyyy-mm-dd`)로 분할된 테이블에서 DDL 옵션을 사용하여 Spark가 겹치지 않는 범위의 파일을 쓰도록 `Write Distributed By Partition Locally Ordered`할 `uuid`수 있습니다.

다음 다이어그램은 테이블이 정렬될 때 열 통계의 효율성이 어떻게 향상되는지 보여줍니다. 이 예제에서 정렬된 테이블은 단일 파일만 열어야 하며 Iceberg의 파티션과 파일의 이점을 최대한 활용할 수 있습니다. 정렬되지 않은 테이블에서는 모든 데이터 파일에가 존재할 `uuid` 수 있으므로 쿼리는 모든 데이터 파일을 열어야 합니다.

![\[Iceberg 테이블에서 정렬 순서 설정\]](http://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/apache-iceberg-on-aws/images/setting-sort-order.png)


정렬 순서를 변경해도 기존 데이터 파일에는 영향을 주지 않습니다. Iceberg 압축을 사용하여 정렬 순서를 적용할 수 있습니다.

다음 그래프와 같이 Iceberg 정렬 테이블을 사용하면 워크로드 비용이 절감될 수 있습니다.

![\[Iceberg 및 Parquet 테이블 비교 비용\]](http://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/apache-iceberg-on-aws/images/cost-graph.png)


이 그래프에는 Iceberg 정렬 테이블과 비교하여 Hive(Parquet) 테이블에 대한 TPC-H 벤치마크를 실행한 결과가 요약되어 있습니다. 그러나 결과는 다른 데이터 세트 또는 워크로드에 따라 다를 수 있습니다.

![\[Parquet 테이블과 Iceberg 테이블의 TPC-H 벤치마크 결과\]](http://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/apache-iceberg-on-aws/images/s3-api-calls.png)
