

# Athena 성능 최적화
<a name="performance-tuning"></a>

이 주제에서는 Athena 쿼리의 성능을 개선하기 위한 일반적인 정보와 구체적인 제안, 그리고 한도 및 리소스 사용량과 관련된 오류를 해결하는 방법을 제공합니다.

대체로 최적화는 서비스, 쿼리, 데이터 구조 범주로 그룹화할 수 있습니다. 서비스 수준, 쿼리 작성 방법, 데이터 및 테이블 구조 등에 대한 결정이 모두 성능에 영향을 미칠 수 있습니다.

**Topics**
+ [서비스 사용 최적화](performance-tuning-service-level-considerations.md)
+ [쿼리 최적화](performance-tuning-query-optimization-techniques.md)
+ [데이터 최적화](performance-tuning-data-optimization-techniques.md)
+ [열 기반 스토리지 형식 사용](columnar-storage.md)
+ [분할 및 버킷팅 사용](ctas-partitioning-and-bucketing.md)
+ [데이터 파티셔닝](partitions.md)
+ [Amazon Athena에서 파티션 프로젝션 사용](partition-projection.md)
+ [Amazon S3 제한 방지](performance-tuning-s3-throttling.md)
+ [추가 리소스](performance-tuning-additional-resources.md)

# 서비스 사용 최적화
<a name="performance-tuning-service-level-considerations"></a>

서비스 수준 고려 사항에는 계정당 실행하는 워크로드 수, Athena뿐만 아니라 전체 서비스에 대한 서비스 할당량, '리소스 부족' 오류를 줄이는 방법에 대한 고려 등이 포함됩니다.

**Topics**
+ [동일한 계정 내에서 여러 워크로드 운영](#performance-tuning-service-quotas)
+ ['리소스 부족' 오류 줄이기](#performance-tuning-resource-limits)

## 동일한 계정 내에서 여러 워크로드 운영
<a name="performance-tuning-service-quotas"></a>

Athena는 할당량을 사용하여 계정 수준에서 쿼리 동시성 및 API 요청 속도를 제한합니다. 이러한 할당량을 초과하면 실행 중 또는 제출 시 쿼리가 실패할 수 있습니다. 이러한 할당량에 대한 자세한 내용은 [Service Quotas](service-limits.md) 섹션을 참조하세요.

동일한 AWS 계정 내에서 여러 워크로드를 운영하는 경우 워크로드는 동일한 계정 수준 할당량에 대해 경합합니다. 예를 들어, 한 워크로드에서 예상치 못한 쿼리 버스트가 발생하면 같은 계정에서 실행되는 다른 워크로드의 대기열 시간이 늘어나거나 최악의 경우 스로틀링으로 인해 쿼리 제출이 실패할 수 있습니다.

CloudWatch를 사용하여 그래프 및 대시보드를 통해 서비스 사용량을 모니터링하는 것이 좋습니다. 또한 사용량이 동시 쿼리 서비스 할당량에 근접하면 알려주는 CloudWatch 경보를 구성하여 할당량 한도에 도달하기 전에 조치를 취할 수 있습니다. 자세한 내용은 [CloudWatch를 사용하여 Athena 사용량 지표 모니터링](monitoring-athena-usage-metrics.md) 섹션을 참조하세요.

계정 내에서 쿼리 동시성을 제어하고 워크로드를 격리하려면 용량 예약을 사용합니다. 용량 예약은 단일 계정 내에서 전용 쿼리 처리 용량을 제공합니다. 용량은 데이터 처리 단위(DPU)로 측정되며 쿼리 동시성을 증가 또는 감소하기 위해 추가 또는 제거할 수 있습니다. 용량 예약을 사용하면 하나 이상의 작업 그룹에 용량을 할당하여 계정 내의 워크로드를 서로 격리할 수 있습니다. 자세한 내용은 [쿼리 처리 용량 관리](capacity-management.md) 섹션을 참조하세요.

관련 없는 여러 워크로드를 서로 다른 AWS 계정으로 분리해야 하지만(예: 개발 환경을 프로덕션 환경에서 격리) 이 접근 방식은 쿼리 동시성을 높이는 확장 가능한 방법을 제공하지 않습니다. 대신 용량 예약을 사용하여 단일 계정 내에서 쿼리 처리 요구 사항을 관리하고 확장할 수 있습니다.

### 다른 서비스의 할당량 고려
<a name="performance-tuning-quotas-in-other-services"></a>

Athena에서 쿼리를 실행할 때 할당량을 적용하는 다른 서비스를 호출할 수 있습니다. 쿼리 실행 중에 Athena는 AWS Glue Data Catalog, Amazon S3, 기타 AWS 서비스(예: IAM 및 AWS KMS)에 대한 API 호출을 수행할 수 있습니다. [페더레이션된 쿼리](federated-queries.md)를 사용하는 경우 Athena는 AWS Lambda도 직접적으로 호출합니다. 이러한 모든 서비스에는 초과할 수 있는 자체 한도 및 할당량이 있습니다. 쿼리 실행 시 이러한 서비스에서 오류가 발생하면 쿼리에 실패하고 소스 서비스에서도 오류가 발생합니다. 복구 가능한 오류는 재시도되지만 문제가 제때 해결되지 않으면 쿼리에 실패할 수 있습니다. 오류 메시지를 자세히 읽고 Athena에서 보낸 것인지, 다른 서비스에서 보낸 것인지 확인합니다. 이 성능 조정 섹션에서는 관련 오류 중 일부를 다룹니다.

Amazon S3 서비스 할당량으로 인한 오류 해결에 대한 자세한 내용은 이 문서에서 나중에 나오는 [너무 많은 수의 파일 방지](performance-tuning-data-optimization-techniques.md#performance-tuning-avoid-having-too-many-files) 섹션을 참조하세요. Amazon S3 성능 최적화에 대한 자세한 내용은 *Amazon S3 사용 설명서*의 [모범 사례 설계 패턴: Amazon S3 성능 최적화](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html)를 참조하세요.

## '리소스 부족' 오류 줄이기
<a name="performance-tuning-resource-limits"></a>

Athena는 분산 쿼리 엔진에서 쿼리를 실행합니다. 쿼리를 제출하면 Athena 엔진 쿼리 플래너에서 쿼리를 실행하는 데 필요한 컴퓨팅 용량을 추정하고 적절히 컴퓨팅 노드 클러스터를 준비합니다. DDL 쿼리와 같은 일부 쿼리는 하나의 노드에서만 실행됩니다. 대규모 데이터 세트에 대한 복잡한 쿼리는 훨씬 더 큰 클러스터에서 실행됩니다. 노드는 동일한 메모리, CPU 및 디스크 구성을 사용하여 균일합니다. Athena는 보다 까다로운 쿼리를 처리하기 위해 스케일 업이 아닌, 스케일 아웃으로 확장합니다.

쿼리 요구량이 쿼리를 실행하는 클러스터에서 사용할 수 있는 리소스를 초과하는 경우가 있습니다. 이 경우 쿼리에 실패하고 Query exhausted resources at this scale factor 오류가 발생합니다.

일반적으로 가장 많이 소진되는 리소스는 메모리이지만, 디스크 공간이 소진되는 경우도 드물게 있습니다. 메모리 오류는 엔진에서 조인 또는 창 함수를 수행할 때 흔히 발생하지만 개별 개수와 집계에서 발생할 수도 있습니다.

일단 'out of resource' 오류로 쿼리에 실패했더라도 다시 실행했을 때 성공할 수도 있습니다. 쿼리 실행은 결정적이지 않습니다. 데이터를 로드하는 데 걸리는 시간, 중간 데이터 세트가 노드에 분산되는 방식 등의 요인으로 인해 리소스 사용량이 달라질 수 있습니다. 예를 들어 두 테이블을 조인하고 조인 조건의 값 분포에서 편차가 큰 쿼리를 가정합니다. 이러한 쿼리는 대부분의 경우 성공할 수 있지만, 가장 일반적인 값이 동일한 노드에서 처리되면 실패합니다.

쿼리가 사용 가능한 리소스를 초과하지 않도록 하려면 이 문서에 언급된 성능 튜닝 팁을 사용합니다. 특히 사용 가능한 리소스를 소진하는 쿼리를 최적화하는 방법에 대한 팁은 [조인 최적화](performance-tuning-query-optimization-techniques.md#performance-tuning-optimizing-joins), [창 함수의 범위를 축소 또는 제거](performance-tuning-query-optimization-techniques.md#performance-tuning-optimizing-window-functions) 및 [근사치를 사용하여 쿼리 최적화](performance-tuning-query-optimization-techniques.md#performance-tuning-optimizing-queries-by-using-approximations) 섹션을 참조하세요.

# 쿼리 최적화
<a name="performance-tuning-query-optimization-techniques"></a>

이 섹션에 설명된 쿼리 최적화 기술을 사용하면 쿼리를 더 빠르게 실행하거나 Athena의 리소스 한도를 초과하는 쿼리 문제를 해결할 수 있습니다.

## 조인 최적화
<a name="performance-tuning-optimizing-joins"></a>

분산 쿼리 엔진에서 조인을 실행하는 다양한 전략이 있습니다. 가장 일반적인 두 가지는 분산 해시 조인과 복잡한 조인 조건을 사용하는 쿼리입니다.

### 분산 해시 조인에서 대형 테이블은 왼쪽에, 소형 테이블은 오른쪽에 배치합니다.
<a name="performance-tuning-distributed-hash-join"></a>

가장 일반적인 유형의 조인은 관계 비교 연산자를 조인 조건으로 사용합니다. Athena는 이 유형의 조인을 분산 해시 조인으로 실행합니다.

분산 해시 조인에서 엔진은 조인 측 중 하나에서 조회 테이블(해시 테이블)을 빌드합니다. 이 조인 측을 *빌드 측*이라고 합니다. 빌드 측 레코드는 여러 노드에 분산되어 있습니다. 각 노드는 해당 하위 세트에 대한 조회 테이블을 빌드합니다. 그러면 조인의 다른 측(*프로브 측*)이 노드를 통해 스트리밍됩니다. 프로브 측의 레코드는 빌드 측과 동일한 방식으로 노드에 분산됩니다. 이를 통해 각 노드는 자체 조회 테이블에서 일치하는 레코드를 조회하여 조인을 수행할 수 있습니다.

조인의 빌드 측에서 생성된 조회 테이블이 메모리에 맞지 않으면 쿼리에 실패할 수 있습니다. 빌드 측의 전체 크기가 사용 가능한 메모리보다 작더라도 레코드 분산 편차가 너무 크면 쿼리에 실패할 수 있습니다. 극단적인 경우 모든 레코드의 조인 조건 값이 같고 단일 노드의 메모리에 맞아야 할 수도 있습니다. 값 세트가 동일한 노드로 전송되고 값의 합계가 사용 가능한 메모리를 초과하면 편차가 적은 쿼리라도 실패할 수 있습니다. 노드는 레코드를 디스크로 유출할 수 있지만 유출 시 쿼리 실행 속도가 느려지고 이 방법으로는 쿼리 실패를 막지 못할 수도 있습니다.

Athena는 더 큰 관계를 프로브 측으로 사용하고 더 작은 관계를 빌드 측으로 사용하도록 조인 순서를 변경하려고 합니다. 그러나 Athena는 테이블의 데이터를 관리하지 않기 때문에 정보가 제한적이며 종종 첫 번째 테이블이 더 크고 두 번째 테이블은 더 작다고 가정해야 합니다.

관계 기반 조인 조건을 사용하여 조인을 작성하는 경우 `JOIN` 키워드 왼쪽의 테이블이 프로브 측이고 오른쪽 테이블이 빌드 측이라고 가정합니다. 오른쪽 테이블(빌드 측)이 더 작은 테이블인지 확인합니다. 조인의 빌드 측 크기를 메모리에 맞도록 작게 설정할 수 없다면 빌드 테이블의 하위 세트를 조인하는 쿼리를 여러 번 실행하는 방법을 고려합니다.

### EXPLAIN을 사용하여 복잡한 조인이 있는 쿼리 분석
<a name="performance-tuning-other-join-types"></a>

복잡한 조인 조건의 쿼리(예: `LIKE`, `>` 또는 다른 연산자를 사용하는 쿼리)는 종종 계산이 까다롭습니다. 최악의 경우에는 조인의 한쪽에 있는 모든 레코드를 조인 반대쪽에 있는 모든 레코드와 비교해야 합니다. 실행 시간은 레코드 수의 제곱에 비례하여 증가하므로 이러한 쿼리는 최대 실행 시간을 초과할 위험이 있습니다.

Athena에서 쿼리를 실행하는 방법을 미리 확인하려면 `EXPLAIN` 문을 사용할 수 있습니다. 자세한 내용은 [Athena에서 EXPLAIN 및 EXPLAIN ANALYZE 사용](athena-explain-statement.md) 및 [Athena EXPLAIN 문 결과 이해](athena-explain-statement-understanding.md)(을)를 참조하세요.

## 창 함수의 범위를 축소 또는 제거
<a name="performance-tuning-optimizing-window-functions"></a>

창 함수는 리소스를 많이 사용하는 작업이므로 쿼리 실행 속도가 느려지거나 쿼리에 실패하고 Query exhausted resources at this scale factor 메시지가 표시될 수 있습니다. 창 함수는 결과를 계산하기 위해 작업하는 모든 레코드를 메모리에 보관합니다. 창 크기가 너무 크면 창 함수의 메모리가 부족해질 수 있습니다.

사용 가능한 메모리 한도 내에서 쿼리를 실행하려면 창 함수가 작동하는 창 크기를 줄입니다. 이렇게 하기 위해 `PARTITIONED BY` 절을 추가하거나 기존 파티셔닝 절의 범위를 좁힐 수 있습니다.

### 창이 아닌 함수를 사용
<a name="performance-tuning-optimizing-window-functions-rewrite"></a>

창 함수가 있는 쿼리를 창 함수 없이 다시 작성할 수도 있습니다. 예를 들어 상위 `N`개 레코드를 찾기 위해 `row_number`를 사용하는 대신, `ORDER BY` 및 `LIMIT`를 사용할 수 있습니다. 레코드 중복을 제거하기 위해 `row_number` 또는 `rank`를 사용하는 대신, [max\$1by](https://trino.io/docs/current/functions/aggregate.html#max_by), [min\$1by](https://trino.io/docs/current/functions/aggregate.html#min_by), [arbitrary](https://trino.io/docs/current/functions/aggregate.html#arbitrary)와 같은 집계 함수를 사용할 수 있습니다.

예를 들어 센서의 업데이트가 포함된 데이터 세트가 있다고 가정합니다. 센서는 주기적으로 배터리 상태를 보고하고 위치와 같은 일부 메타데이터를 포함합니다. 각 센서의 마지막 배터리 상태와 위치를 확인하려는 경우 다음 쿼리를 사용할 수 있습니다.

```
SELECT sensor_id,
       arbitrary(location) AS location,
       max_by(battery_status, updated_at) AS battery_status
FROM sensor_readings
GROUP BY sensor_id
```

위치와 같은 메타데이터는 모든 레코드에서 동일하므로 `arbitrary` 함수를 사용하여 그룹에서 임의의 값을 선택할 수 있습니다.

마지막 배터리 상태를 가져오기 위해 `max_by` 함수를 사용할 수 있습니다. `max_by` 함수는 다른 열의 최댓값을 찾은 레코드에서 열의 값을 선택합니다. 이 경우 그룹 내 마지막 업데이트 시간과 함께 레코드에서 배터리 상태를 반환합니다. 이 쿼리는 창 함수를 사용하는 동등한 쿼리보다 실행 속도가 빠르고 메모리 사용량도 적습니다.

## 집계 최적화
<a name="performance-tuning-optimizing-aggregations"></a>

Athena는 집계를 수행할 때 `GROUP BY` 절의 열을 사용하여 여러 워커 노드에서 레코드를 분산시킵니다. 레코드를 그룹과 일치시키는 작업을 최대한 효율적으로 수행하기 위해 노드에서는 레코드를 메모리에 보관하지만 필요한 경우 디스크로 유출합니다.

`GROUP BY` 절에서 중복 열을 포함하지 않는 것도 좋은 방법입니다. 열 수가 적을수록 필요한 메모리도 적어지기 때문에 더 적은 열을 사용하여 그룹을 설명하는 쿼리가 보다 효율적입니다. 또한 숫자 열은 문자열보다 메모리를 덜 사용합니다. 예를 들어 숫자 카테고리 ID와 카테고리 이름이 모두 있는 데이터 세트를 집계하는 경우 `GROUP BY` 절에서 카테고리 ID 열만 사용합니다.

열이 집계 표현식 또는 `GROUP BY` 절의 일부여야 한다는 점을 처리하기 위해 쿼리에서 `GROUP BY` 절에 열을 포함하기도 합니다. 이 규칙을 따르지 않으면 다음과 같은 오류 메시지가 나타날 수 있습니다.

 EXPRESSION\$1NOT\$1AGGREGATE: line 1:8: 'category' must be an aggregate expression or appear in GROUP BY clause 

`GROUP BY` 절에 중복 열을 추가할 필요가 없도록 다음 예제와 같이 [arbitrary](https://trino.io/docs/current/functions/aggregate.html#arbitrary) 함수를 사용할 수 있습니다.

```
SELECT country_id,
       arbitrary(country_name) AS country_name,
       COUNT(*) AS city_count
FROM world_cities
GROUP BY country_id
```

`ARBITRARY` 함수는 그룹에서 임의의 값을 반환합니다. 이 함수는 그룹 내 모든 레코드에서 열 값이 같지만 값으로 그룹을 식별하지 못하는 경우에 유용합니다.

## 상위 N개 쿼리 최적화
<a name="performance-tuning-optimizing-top-n-queries"></a>

`ORDER BY` 절은 정렬된 순서로 쿼리 결과를 반환합니다. Athena는 분산 정렬을 사용하여 여러 노드에서 정렬 작업을 병렬로 실행합니다.

결과를 정렬할 필요가 없는 경우 `ORDER BY` 절을 추가하지 않습니다. 또한 필요한 쿼리가 아닌 경우 내부 쿼리에 `ORDER BY`를 추가하지 않습니다. 대부분의 경우 쿼리 플래너에서 중복 정렬을 제거할 수 있지만, 보장되지는 않습니다. 이 규칙의 예외는 내부 쿼리가 상위 `N`개 작업(예: 최근 `N`개 또는 가장 일반적인 `N`개 값 찾기)을 수행하는 경우입니다.

Athena에서 `ORDER BY`가 `LIMIT`와 함께 나타나는 경우 상위 `N`개 쿼리를 실행 중이고 적절히 전용 작업을 사용하는 것입니다.

**참고**  
Athena에서 상위 `N`개를 사용하는 `row_number`와 같은 창 함수를 종종 탐지할 수 있어도 `ORDER BY` 및 `LIMIT`를 사용하는 더 간단한 버전을 사용하는 것이 좋습니다. 자세한 내용은 [창 함수의 범위를 축소 또는 제거](#performance-tuning-optimizing-window-functions) 섹션을 참조하세요.

## 필수 열만 포함
<a name="performance-tuning-include-only-required-columns"></a>

열이 필요하지 않은 경우 쿼리에 포함하지 않습니다. 쿼리에서 처리해야 하는 데이터가 적을수록 실행 속도가 빨라집니다. 그러면 필요한 메모리 양과 노드 사이에서 전송해야 하는 데이터 양이 모두 줄어듭니다. 열 기반 파일 형식을 사용하는 경우 열 수를 줄이면 Amazon S3에서 읽는 데이터 양도 줄어듭니다.

Athena에서 결과의 열 수에 대한 구체적인 한도는 없지만 쿼리 실행 방식에 따라 가능한 총 열 크기가 제한됩니다. 총 열 크기에는 이름 및 유형이 포함됩니다.

예를 들어 관계 설명자의 크기 제한을 초과하는 관계로 인해 다음 오류가 발생합니다.

 GENERIC\$1INTERNAL\$1ERROR: io.airlift.bytecode.CompilationException 

이 문제를 해결하려면 쿼리의 열 개수를 줄이거나 하위 쿼리를 만들고 더 적은 양의 데이터를 검색하는 `JOIN`을 사용합니다. 가장 바깥쪽 쿼리에서 `SELECT *`를 수행하는 쿼리가 있는 경우 `*`를 필요한 열만 포함하는 목록으로 변경해야 합니다.

## 근사치를 사용하여 쿼리 최적화
<a name="performance-tuning-optimizing-queries-by-using-approximations"></a>

Athena에서는 개별 값, 가장 빈번한 값, 백분위수(근사 중앙값 포함) 계산 및 히스토그램 생성을 위한 [근사 집계 함수](https://trino.io/docs/current/functions/aggregate.html#appro)를 지원합니다. 정확한 값이 필요하지 않은 경우 이 함수를 사용합니다.

`COUNT(DISTINCT col)` 연산과 달리 [approx\$1distinct](https://trino.io/docs/current/functions/aggregate.html#approx_distinct)는 메모리를 훨씬 적게 사용하고 실행 속도도 더 빠릅니다. 마찬가지로 [histogram](https://trino.io/docs/current/functions/aggregate.html#histogram) 대신 [numeric\$1histogram](https://trino.io/docs/current/functions/aggregate.html#numeric_histogram)을 사용하면 근사치 계산 방법을 사용하므로 메모리가 적게 소비됩니다.

## LIKE 최적화
<a name="performance-tuning-optimizing-like"></a>

`LIKE`를 사용하여 일치하는 문자열을 찾을 수 있지만 문자열이 길면 컴퓨팅 용량을 많이 소비합니다. [regexp\$1like](https://trino.io/docs/current/functions/regexp.html#regexp_like) 함수는 대부분의 경우에 더 빠른 대안이며 유연성이 더 뛰어납니다.

찾고 있는 하위 문자열을 고정하여 검색을 최적화할 수도 있습니다. 예를 들어 접두사를 찾으려면 '%*substr*% 대신 '*substr*%'를 사용하는 것이 훨씬 좋습니다. 또는 `regexp_like`를 사용하는 경우 '^*substr*'을 사용합니다.

## UNION 대신 UNION ALL 사용
<a name="performance-tuning-use-union-all-instead-of-union"></a>

 `UNION ALL` 및 `UNION`은 두 쿼리의 결과를 하나의 결과로 결합하는 두 가지 방법입니다. `UNION ALL`은 첫 번째 쿼리의 레코드를 두 번째 쿼리와 연결합니다. `UNION`은 동일한 작업을 수행하되 중복된 항목도 제거합니다. `UNION`은 모든 레코드를 처리하고 중복을 찾아야 하므로 메모리와 컴퓨팅 용량을 많이 소비하지만, `UNION ALL`은 비교적 속도가 빠른 연산입니다. 레코드의 중복을 제거해야 하는 경우가 아니라면 최상의 성능을 위해 `UNION ALL`을 사용합니다.

## 결과 세트가 큰 경우 UNLOAD 사용
<a name="performance-tuning-use-unload-for-large-result-sets"></a>

쿼리 결과가 커질 것으로 예상되는 경우(예: 수만 행 이상) UNLOAD를 사용하여 결과를 내보냅니다. 대부분의 경우 이 방법이 일반 쿼리를 실행하는 것보다 빠르며 `UNLOAD`를 사용하는 경우 출력을 더 잘 제어할 수 있습니다.

쿼리 실행이 완료되면 Athena는 결과를 압축되지 않은 단일 CSV 파일로 Amazon S3에 저장합니다. 단, 결과가 압축되지 않고 연산을 병렬화할 수 없으므로 `UNLOAD`보다 시간이 오래 걸립니다. 반면 `UNLOAD`는 워커 노드에서 직접 결과를 쓰고 컴퓨팅 클러스터의 병렬 처리를 최대한 활용합니다. 또한 결과를 압축된 형식과 JSON, Parquet 등의 다른 파일 형식으로 기록하도록 `UNLOAD`를 구성할 수 있습니다.

자세한 내용은 [UNLOAD](unload.md) 섹션을 참조하세요.

## CTAS 또는 Glue ETL을 사용하여 자주 사용되는 집계 구체화
<a name="performance-tuning-use-ctas-or-glue-etl-to-materialize-frequently-used-aggregations"></a>

쿼리 '구체화'는 사전 계산된 복잡한 쿼리 결과(예: 집계 및 조인)를 후속 쿼리에서 재사용할 수 있도록 저장하여 쿼리 성능을 가속화하는 방법입니다.

많은 쿼리에 동일한 조인 및 집계가 포함된 경우 공통 하위 쿼리를 새 테이블로 구체화하고 해당 테이블에 대해 쿼리를 실행할 수 있습니다. [쿼리 결과에서 테이블 생성(CTAS)](ctas.md) 또는 [Glue ETL](https://aws.amazon.com/glue)과 같은 전용 ETL 도구를 사용하여 새 테이블을 생성할 수 있습니다.

예를 들어 주문 데이터 세트의 다양한 측면을 보여주는 위젯을 포함하는 대시보드를 가정합니다. 각 위젯에는 자체 쿼리가 있지만 모든 쿼리가 동일한 조인 및 필터를 공유합니다. 주문 테이블은 라인 품목 테이블과 결합되며 지난 3개월만 표시하는 필터가 있습니다. 이러한 쿼리의 일반적인 기능을 식별하면 위젯에서 사용할 수 있는 새 테이블을 생성할 수 있습니다. 그러면 중복이 줄어들고 성능이 향상됩니다. 단점은 새 테이블을 최신 상태로 유지해야 한다는 점입니다.

## 쿼리 결과 재사용
<a name="performance-tuning-reuse-query-results"></a>

동일한 쿼리는 짧은 시간 안에 여러 번 실행하는 것이 일반적입니다. 예를 들어 여러 사람이 같은 데이터 대시보드를 열 때 이런 상황이 발생할 수 있습니다. 이 경우 쿼리를 실행할 때 이전에 계산된 결과를 재사용하도록 Athena에 지시할 수 있습니다. 재사용할 결과의 최대 수명을 지정합니다. 이전에 동일한 쿼리가 해당 기간 안에 실행된 경우 Athena는 쿼리를 다시 실행하는 대신 해당 결과를 반환합니다. 자세한 내용은 *Amazon Athena 사용 설명서*의 [Athena에서 쿼리 결과 재사용](reusing-query-results.md) 섹션 및 *AWS 빅 데이터 블로그*의 [Reduce cost and improve query performance with Amazon Athena Query Result Reuse](https://aws.amazon.com/blogs/big-data/reduce-cost-and-improve-query-performance-with-amazon-athena-query-result-reuse/)를 참조하세요.

# 데이터 최적화
<a name="performance-tuning-data-optimization-techniques"></a>

성능은 쿼리뿐만 아니라 데이터 세트의 구성 방식, 데이터 세트가 사용하는 파일 형식 및 압축에 따라 달라집니다.

## 데이터 파티셔닝
<a name="performance-tuning-partition-your-data"></a>

파티셔닝은 테이블을 여러 부분으로 나누고 날짜, 국가 또는 지역과 같은 속성을 기반으로 관련 데이터를 함께 보관합니다. 파티션 키는 가상 열 역할을 합니다. 테이블 생성 시 파티션 키를 정의하고 쿼리 필터링에 해당 파티션 키를 사용합니다. 파티션 키 열을 필터링하면 일치하는 파티션의 데이터만 읽습니다. 예를 들어 데이터 세트가 날짜별로 파티셔닝되어 있고 쿼리에 지난 주에만 일치하는 필터가 있는 경우 지난 주의 데이터만 읽습니다. 파티셔닝에 대한 자세한 내용은 [데이터 파티셔닝](partitions.md) 섹션을 참조하세요.

## 쿼리를 지원하는 파티션 키를 선택합니다.
<a name="performance-tuning-pick-partition-keys-that-will-support-your-queries"></a>

파티셔닝은 쿼리 성능에 큰 영향을 미치므로 데이터 세트와 테이블을 설계할 때 파티셔닝을 신중하게 고려해야 합니다. 파티션 키가 너무 많으면 너무 작은 파일과 너무 많은 파일로 데이터 세트가 조각화될 수 있습니다. 반대로 파티션 키가 너무 적거나 전혀 파티셔닝하지 않으면 쿼리에서 필요 이상으로 많은 데이터를 스캔합니다.

### 드문 쿼리에 대한 최적화 방지
<a name="performance-tuning-avoid-optimizing-for-rare-queries"></a>

가장 일반적인 쿼리에 대해 최적화하고 드문 쿼리에 대해서는 최적화하지 않는 것이 좋습니다. 예를 들어 쿼리가 일 단위로 표시되는 경우 일부 쿼리가 해당 수준으로 필터링되더라도 시간 단위로 파티셔닝하지 않습니다. 데이터에 세밀한 수준의 타임스탬프 열이 있는 경우 시간 단위로 필터링하는 드문 쿼리에서 이 타임스탬프 열을 사용할 수 있습니다. 드물지만 필요 이상으로 데이터를 조금 더 많이 스캔하더라도 드문 상황에 대비해 전체 성능을 낮추는 작업은 일반적으로 좋은 절충안이 아닙니다.

쿼리에서 스캔해야 하는 데이터를 줄여 성능을 개선하려면 열 기반 파일 형식을 사용하고 레코드를 정렬된 상태로 유지합니다. 시간 단위로 파티셔닝하는 대신 타임스탬프를 기준으로 레코드를 정렬합니다. 기간이 짧은 쿼리의 경우 타임스탬프를 기준으로 정렬하는 방법이 시간 단위로 파티셔닝하는 것만큼 효율적입니다. 또한 타임스탬프를 기준으로 정렬해도 일반적으로 일 단위로 계산되는 기간에서는 쿼리 성능이 저하되지 않습니다. 자세한 내용은 [열 기반 파일 형식 사용](#performance-tuning-use-columnar-file-formats) 섹션을 참조하세요.

수만 개의 파티션이 있는 테이블에 대한 쿼리의 경우 모든 파티션 키에 조건자가 있으면 성능이 개선됩니다. 가장 일반적인 쿼리에 대해 파티셔닝 체계를 설계해야 하는 또 다른 이유가 바로 여기에 있습니다. 자세한 내용은 [관계 조건자로 파티션 쿼리](#performance-tuning-query-partitions-by-equality) 섹션을 참조하세요.

## 파티션 프로젝션 사용
<a name="performance-tuning-use-partition-projection"></a>

파티션 프로젝션은 파티션 정보를 AWS Glue Data Catalog에 저장하지 않고 AWS Glue에서 테이블 속성의 규칙으로 저장하는 Athena 기능입니다. Athena에서 파티션 프로젝션으로 구성된 테이블에 대한 쿼리를 계획할 때 테이블의 파티션 프로젝션 규칙을 읽습니다. Athena에서는 AWS Glue Data Catalog에서 파티션을 조회하는 대신, 쿼리와 규칙을 기반으로 메모리에서 읽을 파티션을 계산합니다.

파티션 프로젝션은 파티션 관리를 단순화하는 것 외에도, 파티션 수가 많은 데이터 세트의 성능을 개선할 수 있습니다. 쿼리에 파티션 키의 특정 값 대신 범위가 포함된 경우 카탈로그에서 일치하는 파티션을 조회할 때 파티션이 많을수록 시간이 더 오래 걸립니다. 파티션 프로젝션을 사용하면 카탈로그로 이동하지 않고도 메모리에서 필터를 계산할 수 있으며 속도도 훨씬 더 빠를 수 있습니다.

경우에 따라 파티션 프로젝션으로 인해 성능이 저하되기도 합니다. 한 가지 예로 '스파스' 테이블이 이에 해당합니다. 스파스 테이블에는 파티션 프로젝션 구성에서 설명하는 파티션 키 값의 모든 순열에 대한 데이터가 포함되지 않습니다. 스파스 테이블을 사용하면 쿼리에서 계산된 파티션 세트와 파티션 프로젝션 구성은 데이터가 없어도 모두 Amazon S3에 나열됩니다.

파티션 프로젝션을 사용하는 경우 모든 파티션 키에 조건자를 포함해야 합니다. 불필요한 Amazon S3 나열을 피하려면 가능한 값의 범위를 좁힙니다. 값의 범위가 백만 개인 파티션 키와 해당 파티션 키에 필터가 없는 쿼리를 가정합니다. 쿼리를 실행하기 위해 Athena는 최소 백만 건의 Amazon S3 나열 작업을 수행해야 합니다. 파티션 프로젝션을 사용하든, 카탈로그에 파티션 정보를 저장하든 관계없이 특정 값을 쿼리할 때 쿼리 속도가 가장 빠릅니다. 자세한 내용은 [관계 조건자로 파티션 쿼리](#performance-tuning-query-partitions-by-equality) 섹션을 참조하세요.

파티션 프로젝션에 대해 테이블을 구성하는 경우 지정하는 범위가 적절해야 합니다. 쿼리에서 파티션 키에 조건자가 포함되지 않는 경우 해당 키 범위의 모든 값이 사용됩니다. 데이터 세트가 특정 날짜에 생성된 경우 해당 날짜를 날짜 범위의 시작점으로 사용합니다. 종료 날짜 범위로는 `NOW`를 사용합니다. 값의 수가 많은 숫자 범위는 피하고 대신 [injected](partition-projection-dynamic-id-partitioning.md#partition-projection-injection) 형식을 사용하는 것이 좋습니다.

파티션 프로젝션에 대한 자세한 내용은 [Amazon Athena에서 파티션 프로젝션 사용](partition-projection.md) 단원을 참조하세요.

## 파티션 인덱스 사용
<a name="performance-tuning-use-partition-indexes"></a>

파티션 인덱스는 파티션 수가 많은 테이블의 파티션 조회 성능을 개선하는 AWS Glue Data Catalog의 기능입니다.

카탈로그에서 파티션 목록은 관계형 데이터베이스의 테이블과 같습니다. 테이블에는 파티션 키에 해당하는 열과 파티션 위치에 해당하는 추가 열이 있습니다. 파티셔닝 테이블을 쿼리하는 경우 이 테이블을 스캔하면 파티션 위치를 조회합니다.

관계형 데이터베이스와 마찬가지로 인덱스를 추가하여 쿼리 성능을 높일 수 있습니다. 여러 인덱스를 추가하여 다양한 쿼리 패턴을 지원할 수 있습니다. AWS Glue Data Catalog 파티션 인덱스는 관계 연산자와 비교 연산자(예: `>`, `>=`, `<`)를 `AND` 연산자로 결합하여 모두 지원합니다. 자세한 내용은 *AWS Glue 개발자 안내서*의 [Working with partition indexes in AWS Glue](https://docs.aws.amazon.com/glue/latest/dg/partition-indexes.html) 및 *AWS 빅 데이터 블로그*의 [Improve Amazon Athena query performance using AWS Glue Data Catalog partition indexes](https://aws.amazon.com/blogs/big-data/improve-amazon-athena-query-performance-using-aws-glue-data-catalog-partition-indexes/)를 참조하세요.

## 파티션 키 유형으로 항상 STRING 사용
<a name="performance-tuning-always-use-string-as-the-type-for-partition-keys"></a>

파티션 키에서 쿼리하는 경우 파티션 필터링을 AWS Glue로 푸시다운하려면 Athena에 `STRING` 유형의 파티션 키가 필요합니다. 파티션 수가 적지 않은 경우 다른 유형을 사용하면 성능이 저하될 수 있습니다. 파티션 키 값이 날짜나 숫자와 비슷한 경우 쿼리에서 적절한 유형으로 변환합니다.

## 오래되고 비어 있는 파티션 제거
<a name="performance-tuning-remove-old-and-empty-partitions"></a>

Amazon S3의 파티션에서 데이터를 제거하는 경우(예: Amazon S3 [수명 주기](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html) 사용) AWS Glue Data Catalog에서도 파티션 항목을 제거해야 합니다. 쿼리 계획 중에 쿼리와 일치하는 모든 파티션이 Amazon S3에 나열됩니다. 빈 파티션이 많은 경우 이러한 파티션을 나열하며 발생하는 오버헤드로 인해 성능이 저하될 수 있습니다.

또한 수천 개의 파티션이 있는 경우 더 이상 관련이 없는 오래된 데이터에 대한 파티션 메타데이터를 제거하는 것이 좋습니다. 예를 들어 쿼리에서 1년이 넘은 데이터를 확인하지 않는 경우 오래된 파티션에 대한 파티션 메타데이터를 주기적으로 제거할 수 있습니다. 파티션 수가 수만 개로 늘어나는 경우 사용하지 않는 파티션을 제거하면 모든 파티션 키에서 조건자를 포함하지 않는 쿼리의 속도를 높일 수 있습니다. 쿼리에서 모든 파티션 키에 조건자를 포함하는 방법에 대한 자세한 내용은 [관계 조건자로 파티션 쿼리](#performance-tuning-query-partitions-by-equality) 섹션을 참조하세요

## 관계 조건자로 파티션 쿼리
<a name="performance-tuning-query-partitions-by-equality"></a>

모든 파티션 키에서 관계 조건자를 포함하는 쿼리는 파티션 메타데이터를 직접 로드할 수 있으므로 실행 속도가 더 빠릅니다. 하나 이상의 파티션 키에 조건자가 없거나 조건자에서 값 범위를 선택하는 쿼리는 피합니다. 이러한 쿼리의 경우 일치하는 값을 찾으려면 모든 파티션 목록을 필터링해야 합니다. 대부분의 테이블에서 이러한 오버헤드가 최소한의 수준이지만, 파티션이 수만 개가 넘는 테이블의 경우 이러한 오버헤드는 심각해질 수 있습니다.

쿼리를 다시 작성하여 파티션을 관계 조건자로 필터링할 수 없는 경우 파티션 프로젝션을 시도할 수 있습니다. 자세한 내용은 [파티션 프로젝션 사용](#performance-tuning-use-partition-projection) 섹션을 참조하세요.

## 파티션 유지 관리에 MSCK REPAIR TABLE 사용 방지
<a name="performance-tuning-avoid-using-msck-repair-table-for-partition-maintenance"></a>

`MSCK REPAIR TABLE`은 실행 시간이 오래 걸릴 수 있으며 새 파티션만 추가하고 오래된 파티션은 제거하지 않으므로 파티션을 효율적으로 관리할 수 있는 방법이 아닙니다([고려 사항 및 제한 사항](msck-repair-table.md#msck-repair-table-considerations) 참조)

파티션은 [AWS Glue Data Catalog API](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api-catalog.html), [ALTER TABLE ADD PARTITION](alter-table-add-partition.md) 또는 [AWS Glue 크롤러](https://docs.aws.amazon.com/glue/latest/dg/crawler-running.html)를 사용하여 수동으로 관리하는 것이 보다 효율적입니다. 다른 방법으로, 파티션 프로젝션을 사용할 수도 있습니다. 그러면 파티션을 모두 관리할 필요가 없습니다. 자세한 내용은 [Amazon Athena에서 파티션 프로젝션 사용](partition-projection.md) 섹션을 참조하세요.

## 쿼리가 파티셔닝 체계와 호환되는지 확인
<a name="performance-tuning-validate-that-your-queries-are-compatible-with-the-partitioning-scheme"></a>

[`EXPLAIN`](athena-explain-statement.md) 문을 사용하여 쿼리에서 스캔할 파티션을 미리 확인할 수 있습니다. 쿼리 앞에 `EXPLAIN` 키워드를 접두사로 추가하고 각 테이블의 `Fragment 2 [SOURCE]` 출력 하단 근처에서 소스 조각(예:`EXPLAIN`)를 조회합니다. 오른쪽이 파티션 키로 정의된 할당을 조회합니다. 아래 줄에는 쿼리가 실행될 때 스캔할 해당 파티션 키의 모든 값 목록이 포함되어 있습니다.

예를 들어 `dt` 파티션 키가 있는 테이블에 쿼리가 있고 이 쿼리에 `EXPLAIN` 접두사를 추가한다고 가정합니다. 쿼리의 값이 날짜이고 필터로 3일 범위를 선택하는 경우 `EXPLAIN` 출력은 다음과 비슷합니다.

```
dt := dt:string:PARTITION_KEY
    :: [[2023-06-11], [2023-06-12], [2023-06-13]]
```

`EXPLAIN` 출력에서는 플래너가 이 파티션 키에 대해 쿼리와 일치하는 세 개의 값을 찾았음을 보여줍니다. 또한 해당 값도 표시합니다. `EXPLAIN` 사용에 대한 자세한 내용은 [Athena에서 EXPLAIN 및 EXPLAIN ANALYZE 사용](athena-explain-statement.md) 및 [Athena EXPLAIN 문 결과 이해](athena-explain-statement-understanding.md) 섹션을 참조하세요.

## 열 기반 파일 형식 사용
<a name="performance-tuning-use-columnar-file-formats"></a>

Parquet 및 ORC와 같은 열 기반 파일 형식은 분산 분석 워크로드를 위해 설계되었습니다. 이 파일 형식에서는 데이터를 행 대신 열을 기준으로 구성합니다. 데이터를 열 형식으로 구성하면 다음과 같은 이점이 있습니다.
+ 쿼리에 필요한 열만 로드됨
+ 로드해야 하는 전체 데이터 양이 감소함
+ 열 값이 함께 저장되므로 데이터를 효율적으로 압축할 수 있음 
+ 엔진에서 불필요한 데이터를 로드하지 않고 건너뛸 수 있도록 하는 메타데이터가 파일에 포함될 수 있음

파일 메타데이터를 사용하는 방법의 예로, 파일 메타데이터에서 데이터 페이지에 최솟값 및 최댓값에 대한 정보가 포함될 수 있습니다. 쿼리된 값이 메타데이터에 명시된 범위를 벗어나면 페이지를 건너뛸 수 있습니다.

이 메타데이터를 사용하여 성능을 향개선하는 한 가지 방법은 파일 내의 데이터를 정렬하는 것입니다. 예를 들어 `created_at` 항목이 짧은 기간에 포함된 레코드를 찾는 쿼리를 가정합니다. 데이터가 `created_at` 열을 기준으로 정렬된 경우 Athena는 파일 메타데이터의 최솟값과 최댓값을 사용하여 데이터 파일의 불필요한 부분을 건너뛸 수 있습니다.

열 기반 파일 형식을 사용하는 경우 파일 크기가 너무 작지 않아야 합니다. [너무 많은 수의 파일 방지](#performance-tuning-avoid-having-too-many-files)에서 설명한 것처럼 작은 파일이 많은 데이터 세트는 성능 문제를 일으킬 수 있습니다. 열 기반 파일 형식에서 특히 그렇습니다. 크기가 작은 파일의 경우 열 기반 파일 형식에서 오버헤드의 단점이 장점보다 더 큽니다.

Parquet 및 ORC는 내부적으로 행 그룹(Parquet) 및 스트라이프(ORC)로 구성됩니다. 행 그룹의 기본 크기는 128MB이고 스트라이프의 경우 64MB입니다. 열이 많은 경우 더 나은 성능을 위해 행 그룹과 스트라이프 크기를 늘릴 수 있습니다. 행 그룹 또는 스트라이프 크기를 기본값보다 작게 줄이는 것은 권장되지 않습니다.

다른 데이터 형식을 Parquet 또는 ORC로 변환하기 위해 AWS Glue ETL 또는 Athena를 사용할 수 있습니다. 자세한 내용은 [열 기반 형식으로 변환](columnar-storage.md#convert-to-columnar) 섹션을 참조하세요.

## 데이터 압축
<a name="performance-tuning-compress-data"></a>

Athena에서는 다양한 압축 형식을 지원합니다. 압축을 해제하기 전에 스캔한 바이트 수만큼 비용이 청구되므로 압축된 데이터를 쿼리하는 것이 더 빠르고 비용도 저렴합니다.

[gzip](https://www.gnu.org/software/gzip/) 형식은 뛰어난 압축률을 제공하며 다른 여러 도구 및 서비스에서 폭넓게 지원됩니다. [zstd](https://facebook.github.io/zstd/)(Zstandard) 형식은 성능과 압축률 간 균형이 잘 잡힌 최신 압축 형식입니다.

JSON 및 CSV 데이터와 같은 텍스트 파일을 압축할 때는 파일 수와 파일 크기 사이의 균형을 맞춥니다. 대부분의 압축 형식을 사용하려면 처음부터 리더에서 파일을 읽어야 합니다. 즉, 압축된 텍스트 파일은 일반적으로 병렬로 처리할 수 없습니다. 압축되지 않은 큰 파일은 쿼리 처리 중에 병렬 처리 성능을 높이기 위해 종종 작업자 사이에서 분할되기도 하지만, 대부분의 압축 형식에서는 지원되지 않습니다.

[너무 많은 수의 파일 방지](#performance-tuning-avoid-having-too-many-files)에서 설명한 것처럼 파일이 너무 많거나 너무 적지 않은 것이 좋습니다. 파일 수는 쿼리를 처리할 수 있는 작업자 수의 한도이므로 이 규칙은 특히 압축된 파일에 적용됩니다.

Athena에서 압축 사용에 대한 자세한 내용은 [Athena에서 압축 사용](compression-formats.md) 섹션을 참조하세요.

## 카디널리티가 높은 키에서 조회할 때 버킷팅 사용
<a name="performance-tuning-use-bucketing-for-lookups-on-keys-with-high-cardinality"></a>

버킷팅은 열 중 하나의 값을 기준으로 레코드를 별도의 파일로 분산하는 기법입니다. 이렇게 하면 값이 같은 모든 레코드가 같은 파일에 보관됩니다. 버킷팅은 카디널리티가 높은 키가 있고 많은 쿼리에서 해당 키의 특정 값을 조회하는 경우에 유용합니다.

예를 들어 특정 사용자에 대한 레코드 세트를 쿼리한다고 가정합니다. 데이터가 사용자 ID로 버킷팅되는 경우 Athena는 특정 ID에 대한 레코드를 포함하는 파일과 그렇지 않은 파일을 미리 파악합니다. 이를 통해 Athena가 ID를 포함할 수 있는 파일만 읽을 수 있으므로 읽는 데이터의 양이 크게 줄어듭니다. 또한 특정 ID를 찾기 위해 데이터를 검색하는 데 필요한 컴퓨팅 시간도 줄어듭니다.

### 쿼리에서 열의 여러 값을 자주 검색하는 경우 버킷팅 방지
<a name="performance-tuning-disadvantages-of-bucketing"></a>

쿼리에서 데이터가 버킷팅되는 열의 여러 값을 자주 검색하는 경우에는 버킷팅의 효율성이 떨어집니다. 쿼리되는 값이 많을수록 전체 또는 대부분의 파일을 읽어야 할 가능성이 커집니다. 예를 들어 버킷이 3개이고 쿼리에서 세 개의 다른 값을 조회하는 경우 모든 파일을 읽어야 할 수 있습니다. 버킷팅은 쿼리에서 단일 값을 조회할 때 가장 효과적입니다.

자세한 내용은 [분할 및 버킷팅 사용](ctas-partitioning-and-bucketing.md) 섹션을 참조하세요.

## 너무 많은 수의 파일 방지
<a name="performance-tuning-avoid-having-too-many-files"></a>

많은 수의 작은 파일로 구성된 데이터 세트는 전체 쿼리 성능을 저하시킵니다. Athena는 쿼리를 계획할 때 모든 파티션 위치를 나열하므로 시간이 걸립니다. 각 파일을 처리하고 요청하는 데에도 계산 오버헤드가 발생합니다. 따라서 Amazon S3에서 큰 파일 하나를 로드하는 것이 많은 수의 작은 파일에서 동일한 레코드를 로드하는 것보다 빠릅니다.

극단적인 경우에는 Amazon S3 서비스 한도에 도달할 수 있습니다. Amazon S3는 단일 인덱스 파티션에 대해 초당 최대 5,500개의 요청을 지원합니다. 처음에는 버킷을 단일 인덱스 파티션으로 취급하지만 요청 로드가 증가하면 여러 인덱스 파티션으로 분할할 수 있습니다.

Amazon S3는 요청 패턴을 확인하고 키 접두사를 기반으로 분할합니다. 데이터 세트가 수천 개의 파일로 구성된 경우 Athena에서 수신되는 요청이 요청 할당량을 초과할 수 있습니다. 파일 수가 적더라도 동일한 데이터 세트에 대해 여러 개의 동시 쿼리를 수행하면 할당량을 초과할 수 있습니다. 동일한 파일에 액세스하는 다른 애플리케이션이 총 요청 수에 영향을 미칠 수도 있습니다.

요청 속도(`limit`)를 초과하면 Amazon S3는 다음 오류를 반환합니다. 이 오류는 Athena에서 쿼리 상태 정보에 포함됩니다.

 SlowDown: Please reduce your request rate 

문제를 해결하려면 먼저 오류가 단일 쿼리에서 발생했는지, 아니면 동일한 파일을 읽는 여러 쿼리에서 발생했는지 확인합니다. 후자인 경우 쿼리가 동시에 실행되지 않도록 쿼리 실행을 조정합니다. 이를 위해 애플리케이션에 대기열 메커니즘을 추가하거나 재시도를 추가합니다.

단일 쿼리를 실행할 때 오류가 발생하는 경우 데이터 파일을 결합하거나 읽는 파일 수를 줄이도록 쿼리를 수정합니다. 작은 파일을 결합하기에 가장 좋은 시점은 파일을 쓰기 전입니다. 이를 위해 다음 기법을 고려합니다.
+ 더 큰 파일을 쓰도록 파일을 쓰는 프로세스를 변경합니다. 예를 들어 레코드를 쓰기 전에 더 오래 레코드를 버퍼링할 수 있습니다.
+ Amazon S3의 한 위치에 파일을 배치하고 Glue ETL과 같은 도구를 사용하여 파일을 더 큰 파일로 결합합니다. 그런 다음 큰 파일을 테이블이 가리키는 위치로 이동합니다. 자세한 내용은 *AWS Glue 개발자 안내서*의 [Reading input files in larger groups](https://docs.aws.amazon.com/glue/latest/dg/grouping-input-files.html) 및 *AWS re:Post 지식 센터*의 [더 큰 파일을 출력하도록 AWS Glue ETL 작업을 구성하려면 어떻게 해야 합니까?](https://repost.aws/knowledge-center/glue-job-output-large-files)를 참조하세요.
+ 파티션 키 수를 줄입니다. 파티션 키가 너무 많으면 각 파티션에 레코드 수가 적어 작은 파일 수가 너무 많아질 수 있습니다. 생성할 파티션 결정에 대한 자세한 내용은 [쿼리를 지원하는 파티션 키를 선택합니다.](#performance-tuning-pick-partition-keys-that-will-support-your-queries) 섹션을 참조하세요.

## 파티션 이외의 추가 스토리지 계층 구조 방지
<a name="performance-tuning-avoid-additional-storage-hierarchies-beyond-the-partition"></a>

쿼리 계획 오버헤드를 방지하려면 각 파티션 위치에 파일을 플랫 구조로 저장합니다. 추가 디렉터리 계층 구조는 사용하지 않습니다.

Athena는 쿼리를 계획할 때 쿼리와 일치하는 모든 파티션의 모든 파일을 나열합니다. Amazon S3에 디렉터리 자체는 없지만, `/` 슬래시를 디렉터리 구분 기호로 해석하는 것이 규칙입니다. Athena는 파티션 위치를 나열할 때 찾은 모든 디렉터리를 반복적으로 나열합니다. 파티션 내 파일을 계층 구조로 구성하면 여러 차례 나열이 수행됩니다.

모든 파일이 파티션 위치에 바로 있으면 대부분의 경우 한 번의 나열 작업만 수행하면 됩니다. 그러나 Amazon S3는 나열 작업당 1,000개의 객체만 반환하므로 파티션에 1,000개가 넘는 파일이 있으면 순차 나열 작업이 여러 차례 필요합니다. 파티션에 1,000개가 넘는 파일이 있으면 더 심각한 다른 성능 문제가 발생할 수도 있습니다. 자세한 내용은 [너무 많은 수의 파일 방지](#performance-tuning-avoid-having-too-many-files) 섹션을 참조하세요.

## 필요한 경우에만 SymlinkTextInputFormat 사용
<a name="performance-tuning-use-symlinktextinputformat-only-when-necessary"></a>

[https://athena.guide/articles/stitching-tables-with-symlinktextinputformat](https://athena.guide/articles/stitching-tables-with-symlinktextinputformat) 기법을 사용하면 테이블의 파일이 파티션으로 깔끔하게 구성되지 않은 상황을 해결할 수 있습니다. 예를 들어 모든 파일에서 접두사가 같거나 스키마가 다른 파일이 같은 위치에 있는 경우 symlink가 유용할 수 있습니다.

하지만 symlink를 사용하면 쿼리 실행이 더 간접적으로 이루어집니다. 이렇게 간접적으로 이루어진 쿼리 실행은 전체 성능에 영향을 미칩니다. symlink 파일을 읽고 symlink 파일이 정의한 위치를 나열해야 합니다. 이로 인해 일반적인 Hive 테이블에서는 필요하지 않은 여러 번의 왕복 작업이 Amazon S3에 추가됩니다. 결론적으로, 파일 재구성과 같은 더 나은 옵션을 사용할 수 없는 경우에만 `SymlinkTextInputFormat`을 사용해야 합니다.

# 열 기반 스토리지 형식 사용
<a name="columnar-storage"></a>

[Apache Parquet](https://parquet.apache.org) 및 [ORC](https://orc.apache.org/)는 빠른 데이터 검색에 최적화되어 있고, AWS 분석 애플리케이션에서 사용되는 열 기반 스토리지 형식입니다.

열 기반 스토리지 형식에는 Athena에서 사용하는 데 적합하게 만드는 다음과 같은 특징이 있습니다.
+ *열 기준 압축, 열 데이터 유형에 대해 선택한 압축 알고리즘 사용*: Amazon S3에서 스토리지 공간을 절약하고 쿼리 처리 중 디스크 공간 및 I/O를 줄입니다.
+ Parquet 및 ORC의 *조건자 푸시다운*: Athena에서 필요한 블록만 가져오도록 쿼리할 수 있어 쿼리 성능을 높입니다. Athena 쿼리가 데이터에서 특정 열 값을 얻으면 데이터 블록 조건자의 통계(예: 최대값/최소값)를 사용해 블록을 읽거나 건너뛸지 결정합니다.
+ Parquet 및 ORC의 *데이터 분할*: Athena에서 쿼리 처리 중 데이터 읽기를 여러 리더에게 분할해 병렬 처리를 늘립니다.

기존 원시 데이터를 다른 스토리지 형식에서 Parquet 또는 ORC로 변환하려면 Athena에서 [CREATE TABLE AS SELECT (CTAS)](ctas.md) 쿼리를 실행하고 데이터 스토리지 형식을 Parquet 또는 ORC로 지정하거나 AWS Glue 크롤러를 사용합니다.

## Parquet 및 ORC 중에서 선택
<a name="columnar-storage-choosing"></a>

Optimized Row Columnar(ORC) 및 Parquet 중에서의 선택은 특정 사용 요구 사항에 따라 다릅니다.

Apache Parquet은 효율적인 데이터 압축 및 인코딩 체계를 제공하며 복잡한 쿼리를 실행하고 대량의 데이터를 처리하는 데 적합합니다. Parquet은 [Apache Arrow](https://arrow.apache.org/)와 함께 사용하도록 최적화되어 있으므로 Arrow 관련 도구를 사용하는 경우 더 유용합니다.

ORC는 Hive 데이터를 저장하는 효율적인 방법을 제공합니다. ORC 파일은 종종 Parquet 파일보다 크기가 작으며 ORC 인덱스를 사용하면 쿼리 속도를 높일 수 있습니다. 또한 ORC는 구조체, 맵, 목록과 같은 복잡한 유형을 지원합니다.

Parquet 및 ORC 중에서 선택할 때는 다음을 고려합니다.

**쿼리 성능** - Parquet은 더 광범위한 쿼리 유형을 지원하므로 복잡한 쿼리를 수행하려는 경우 Parquet이 더 적합합니다.

**복잡한 데이터 유형** - 복잡한 데이터 유형을 사용하는 경우 다양한 복잡한 데이터 유형을 지원하는 ORC가 더 적합합니다.

**파일 크기** - 디스크 공간이 우려되는 경우 일반적으로 ORC에서는 파일 크기가 더 작으므로 스토리지 비용을 절감할 수 있습니다.

**압축** - Parquet 및 ORC 모두 뛰어난 압축률을 제공하지만 최적의 형식은 특정 사용 사례에 따라 달라질 수 있습니다.

**진화** - Parquet과 ORC 모두 스키마 진화를 지원하므로 시간이 지남에 따라 열을 추가, 제거 또는 수정할 수 있습니다.

Parquet 및 ORC 모두 빅 데이터 애플리케이션에 적합하지만 선택하기 전에 시나리오의 요구 사항을 고려합니다. 데이터 및 쿼리에 대한 벤치마크를 수행하여 사용 사례에 더 적합한 형식을 확인하는 것이 좋습니다.

## 열 기반 형식으로 변환
<a name="convert-to-columnar"></a>

JSON 또는 CSV와 같은 소스 데이터를 열 형식으로 쉽게 변환할 수 있는 옵션을 통해 [CREATE TABLE AS](ctas.md) 쿼리를 사용하거나 AWS Glue의 작업을 실행할 수 있습니다.
+ `CREATE TABLE AS`(CTAS) 쿼리를 통해 한 번에 데이터를 Parquet 또는 ORC로 변환할 수 있습니다. 예는 [CTAS 쿼리 예제](ctas-examples.md) 페이지의 [예제: 쿼리 결과를 다른 형식으로 쓰기](https://docs.aws.amazon.com/athena/latest/ug/ctas-examples.html#ctas-example-format)를 참조하세요.
+ ETL에 Athena를 사용하여 데이터를 CSV에서 Parquet으로 변환하는 방법에 대한 자세한 내용은 [ETL 및 데이터 분석에 CTAS 및 INSERT INTO 사용](ctas-insert-into-etl.md) 섹션을 참조하세요.
+ AWS Glue 작업을 실행하여 CSV 데이터를 Parquet으로 변환하는 방법에 대한 자세한 내용은 AWS 빅 데이터 블로그 게시물 [Amazon S3 및 AWS Glue를 이용한 데이터 레이크 구축하기](https://aws.amazon.com/blogs/big-data/build-a-data-lake-foundation-with-aws-glue-and-amazon-s3/)의 'CSV에서 Parquet 형식으로 데이터 변환' 섹션을 참조하세요. AWS Glue에서는 동일한 기술을 사용하여 CSV 데이터를 ORC로 변환하거나 JSON 데이터를 Parquet 또는 ORC로 변환할 수 있습니다.

# 분할 및 버킷팅 사용
<a name="ctas-partitioning-and-bucketing"></a>

파티셔닝과 버킷팅은 Athena에서 쿼리를 실행할 때 스캔해야 하는 데이터의 양을 줄이는 두 가지 방법입니다. 파티셔닝 및 버킷팅은 상호 보완적이며 함께 사용할 수 있습니다. 스캔하는 데이터의 양을 줄이면 성능이 향상되고 비용이 절감됩니다. Athena 쿼리 성능에 대한 일반적인 지침은 [Top 10 performance tuning tips for Amazon Athena](https://aws.amazon.com/blogs/big-data/top-10-performance-tuning-tips-for-amazon-athena/)를 참조하세요.

**Topics**
+ [파티셔닝이란 무엇인가요?](ctas-partitioning-and-bucketing-what-is-partitioning.md)
+ [버킷팅이란 무엇인가요?](ctas-partitioning-and-bucketing-what-is-bucketing.md)
+ [추가 리소스](ctas-partitioning-and-bucketing-additional-resources.md)

# 파티셔닝이란 무엇인가요?
<a name="ctas-partitioning-and-bucketing-what-is-partitioning"></a>

파티셔닝이란 데이터의 특정 속성을 기반으로 Amazon S3의 디렉터리(또는 '접두사')로 데이터를 구성하는 작업을 의미합니다. 이러한 속성을 파티션 키라고 합니다. 일반적인 파티션 키는 날짜 또는 기타 시간 단위(예: 연도 또는 월)입니다. 하지만 데이터 세트는 둘 이상의 키로 파티셔닝할 수 있습니다. 예를 들어 제품 판매에 대한 데이터를 날짜, 제품 카테고리 및 마켓에 따라 파티셔닝할 수 있습니다.

## 파티셔닝 방법 결정
<a name="ctas-partitioning-and-bucketing-deciding-how-to-partition"></a>

쿼리에 항상 또는 자주 사용되고 카디널리티가 낮은 속성이 파티션 키로 사용하기에 적합합니다. 파티셔닝이 너무 많은 경우와 너무 적은 경우 사이에는 상충 관계가 있습니다. 파티션이 너무 많으면 파일 수가 증가하여 오버헤드가 발생합니다. 파티션 자체를 필터링하는 데 약간의 오버헤드도 발생합니다. 파티션이 너무 적으면 쿼리에서 더 많은 데이터를 스캔해야 하기도 합니다.

## 분할된 테이블 생성
<a name="ctas-partitioning-and-bucketing-creating-a-partitioned-table"></a>

데이터 세트가 파티셔닝되면 Athena에서 파티셔닝된 테이블을 생성할 수 있습니다. 파티셔닝된 테이블은 파티션 키가 있는 테이블입니다. `CREATE TABLE`을 사용하는 경우 테이블에 파티션을 추가합니다. `CREATE TABLE AS`을 사용하는 경우 Amazon S3에 생성된 파티션이 테이블에 자동으로 추가됩니다.

`CREATE TABLE` 문에서 `PARTITIONED BY (column_name data_type)` 절에 파티션 키를 지정합니다. `CREATE TABLE AS`에서는 `WITH (partitioned_by = ARRAY['partition_key'])` 절에 파티션 키를 지정하거나 Iceberg 테이블의 경우 `WITH (partitioning = ARRAY['partition_key'])` 절에 지정합니다. 성능을 고려하여 파티션 키는 항상 `STRING` 유형이어야 합니다. 자세한 내용은 [파티션 키의 데이터 형식으로 문자열 사용](data-types-timestamps.md#data-types-timestamps-partition-key-types) 섹션을 참조하세요.

추가적인 `CREATE TABLE` 및 `CREATE TABLE AS` 구문 세부 정보는 [CREATE TABLE](create-table.md) 및 [CTAS 테이블 속성](create-table-as.md#ctas-table-properties) 섹션을 참조하세요.

## 분할된 테이블 쿼리
<a name="ctas-partitioning-and-bucketing-querying-partitioned-tables"></a>

파티셔닝된 테이블을 쿼리하면 Athena는 쿼리에서 조건자를 사용하여 파티션 목록을 필터링합니다. 그런 다음 일치하는 파티션의 위치를 사용하여 찾은 파일을 처리합니다. Athena는 쿼리 조건자와 일치하지 않는 파티션의 데이터를 읽지 않음으로써 스캔하는 데이터의 양을 효율적으로 줄일 수 있습니다.

### 예제
<a name="ctas-partitioning-and-bucketing-partitioned-table-example-queries"></a>

`sales_date` 및 `product_category`를 기준으로 파티셔닝된 테이블이 있고 특정 카테고리에서 1주일 간의 총 수익을 알고자 합니다. Athena가 다음 예제와 같이 최소의 데이터만 스캔하도록 하기 위해 `sales_date` 및 `product_category` 열에 조건자를 포함합니다.

```
SELECT SUM(amount) AS total_revenue 
FROM sales 
WHERE sales_date BETWEEN '2023-02-27' AND '2023-03-05' 
AND product_category = 'Toys'
```

데이터 세트가 날짜를 기준으로 파티셔닝되었지만 정밀한 타임스탬프도 있다고 가정합니다.

Iceberg 테이블을 사용하면 열과 관계를 설정하도록 파티션 키를 선언할 수 있지만, Hive 테이블을 사용하면 쿼리 엔진이 열과 파티션 키 간 관계를 인식하지 못합니다. 따라서 쿼리에서 필요한 것보다 많은 데이터를 스캔하지 않도록 하려면 쿼리에서 열과 파티션 키 모두에 조건자를 포함해야 합니다.

예를 들어 이전 예제의 `sales` 테이블에도 해당 `TIMESTAMP` 데이터 형식의 `sold_at` 열이 있다고 가정합니다. 특정 시간 범위의 매출만 원하는 경우 다음과 같이 쿼리를 작성합니다.

```
SELECT SUM(amount) AS total_revenue 
FROM sales 
WHERE sales_date = '2023-02-28' 
AND sold_at BETWEEN TIMESTAMP '2023-02-28 10:00:00' AND TIMESTAMP '2023-02-28 12:00:00' 
AND product_category = 'Toys'
```

Hive 테이블과 Iceberg 테이블 쿼리 사이의 이러한 차이에 대한 자세한 내용은 [시간을 기준으로 파티셔닝된 타임스탬프 필드에 대한 쿼리를 작성하는 방법](data-types-timestamps.md#data-types-timestamps-how-to-write-queries-for-timestamp-fields-that-are-also-time-partitioned) 섹션을 참조하세요.

# 버킷팅이란 무엇인가요?
<a name="ctas-partitioning-and-bucketing-what-is-bucketing"></a>

버킷팅은 데이터 세트의 레코드를 버킷이라는 카테고리로 구성하는 방법입니다.

버킷 및 버킷팅의 의미는 서로 다르며, Amazon S3 버킷과도 혼동해서는 안 됩니다. 데이터 버킷팅에서 속성 값이 동일한 레코드는 동일한 버킷으로 이동합니다. 레코드는 각 버킷이 대략 같은 양의 데이터를 보유하도록 버킷 사이에서 최대한 균등하게 분산됩니다.

실제로 버킷은 파일이고 해시 함수에서 레코드를 배치할 버킷을 결정합니다. 버킷팅된 데이터 세트에는 파티션 1개에 버킷당 하나 이상의 파일이 있습니다. 파일이 속한 버킷은 파일 이름에서 인코딩됩니다.

## 버킷팅의 장점
<a name="ctas-partitioning-and-bucketing-bucketing-benefits"></a>

버킷팅은 데이터 세트가 특정 속성을 기준으로 버킷팅되고 해당 속성에 특정 값이 있는 레코드를 검색하려는 경우에 유용합니다. 데이터가 버킷팅되어 있기 때문에 Athena는 값을 사용하여 찾을 파일을 결정할 수 있습니다. 예를 들어 데이터 세트가 `customer_id`를 기준으로 버킷팅되었고 특정 고객에 대한 모든 레코드를 찾고 싶다고 가정합니다. Athena는 이러한 레코드를 포함하는 버킷을 확인하고 해당 버킷의 파일만 읽습니다.

카디널리티가 높고(즉, 개별 값이 많음) 열이 균등하게 분산되었으며 특정 값을 자주 쿼리하는 열이 있을 때 버킷팅에 적합합니다.

**참고**  
Athena는 버킷팅된 테이블에 새 레코드를 추가할 때 `INSERT INTO` 사용을 지원하지 않습니다.

## 버킷팅된 열에 대한 필터링에 지원되는 데이터 형식
<a name="ctas-partitioning-and-bucketing-data-types-supported-for-filtering-on-bucketed-columns"></a>

특정 데이터 형식으로 버킷팅된 열에 필터를 추가할 수 있습니다. Athena는 다음과 같은 데이터 형식의 버킷팅된 열에서 필터링을 지원합니다.
+ BOOLEAN
+ BYTE
+ DATE
+ DOUBLE
+ FLOAT
+ INT
+ LONG
+ SHORT
+ STRING
+ VARCHAR

## Hive 및 Spark 지원
<a name="ctas-partitioning-and-bucketing-hive-and-spark-support"></a>

Athena 엔진 버전 2는 Hive 버킷 알고리즘을 사용하여 버킷팅된 데이터 세트를 지원하며 Athena 엔진 버전 3은 Apache Spark 버킷팅 알고리즘도 지원합니다. Hive 버킷팅이 기본값입니다. Spark 알고리즘을 사용하여 데이터 세트를 버킷팅하는 경우 `TBLPROPERTIES` 절을 사용하여 `bucketing_format` 속성 값을 `spark`로 설정합니다.

**참고**  
Athena는 `CREATE TABLE AS SELECT`([CTAS](ctas.md)) 쿼리당 파티션을 100개로 제한합니다. 마찬가지로, [INSERT INTO](insert-into.md) 문을 통해 대상 테이블에 최대 100개의 파티션만 추가할 수 있습니다.  
이 제한을 초과하면 “HIVE\$1TOO\$1MANY\$1OPEN\$1파티션: 파티션/버킷에 대해 열린 작성자 100개를 초과했습니다.”라는 오류 메시지가 표시될 수 있습니다. 이러한 제한은 CTAS 문(최대 100개의 파티션 생성) 및 일련의 `INSERT INTO` 문(각각 최대 100개의 파티션 삽입)을 사용하여 해결할 수 있습니다. 자세한 내용은 [CTAS 및 INSERT INTO를 사용하여 100개 파티션 한도 문제 해결](ctas-insert-into.md) 섹션을 참조하세요.

## CREATE TABLE 버킷팅 예제
<a name="ctas-partitioning-and-bucketing-bucketing-create-table-example"></a>

기존의 버킷팅된 데이터 세트에 대한 테이블을 생성하려면 `CLUSTERED BY (column)` 절 및 `INTO N BUCKETS`절을 차례로 사용합니다. `INTO N BUCKETS` 절은 데이터가 버킷팅되는 버킷 수를 지정합니다.

다음 `CREATE TABLE` 예제에서는 Spark 알고리즘을 사용하여 `customer_id`를 기준으로 `sales` 데이터 세트를 8개의 버킷으로 버킷팅합니다. `CREATE TABLE` 문에서는 `CLUSTERED BY` 및 `TBLPROPERTIES` 절을 사용하여 속성을 적절하게 설정합니다.

```
CREATE EXTERNAL TABLE sales (...) 
... 
CLUSTERED BY (`customer_id`) INTO 8 BUCKETS 
... 
TBLPROPERTIES ( 
  'bucketing_format' = 'spark' 
)
```

## CREATE TABLE AS(CTAS) 버킷팅 예제
<a name="ctas-partitioning-and-bucketing-bucketing-create-table-as-example"></a>

`CREATE TABLE AS`를 사용하여 버킷팅을 지정하려면 다음 예제와 같이 `bucketed_by` 및 `bucket_count` 파라미터를 사용합니다.

```
CREATE TABLE sales 
WITH ( 
  ... 
  bucketed_by = ARRAY['customer_id'], 
  bucket_count = 8 
) 
AS SELECT ...
```

## 버킷팅 쿼리 예제
<a name="ctas-partitioning-and-bucketing-bucketing-query-example"></a>

다음 쿼리 예제는 특정 고객이 1주일 동안 구매한 제품의 이름을 찾습니다.

```
SELECT DISTINCT product_name 
FROM sales 
WHERE sales_date BETWEEN '2023-02-27' AND '2023-03-05' 
AND customer_id = 'c123'
```

이 테이블이 `sales_date`를 기준으로 파티셔닝되고 `customer_id`를 기준으로 버키팅된 경우 Athena는 고객 레코드가 들어 있는 버킷을 계산할 수 있습니다. Athena는 파티션당 최대 하나의 파일 정도만 읽습니다.

# 추가 리소스
<a name="ctas-partitioning-and-bucketing-additional-resources"></a>
+ 버킷팅 및 파티셔닝된 테이블 모두를 생성하는 `CREATE TABLE AS` 예제는 [예제: 버킷팅 및 파티셔닝된 테이블 생성](https://docs.aws.amazon.com/athena/latest/ug/ctas-examples.html#ctas-example-bucketed)을 참조하세요.
+ Athena CTAS 명령문, Apache Spark용 AWS Glue 및 Apache Iceberg 테이블에 대한 버킷팅 등 AWS 데이터 레이크에 버킷팅을 구현하는 방법에 자세한 내용은 AWS 빅 데이터 블로그 게시물 [Optimize data layout by bucketing with Amazon Athena and AWS Glue to accelerate downstream queries](https://aws.amazon.com/blogs/big-data/optimize-data-layout-by-bucketing-with-amazon-athena-and-aws-glue-to-accelerate-downstream-queries/)를 참조하세요.

# 데이터 파티셔닝
<a name="partitions"></a>

데이터를 분할하면 각 쿼리가 스캔하는 데이터의 양을 제한하여 성능을 향상시키고 비용을 절감할 수 있습니다. 어떤 키를 기준으로도 데이터를 분할할 수 있습니다. 일반적으로 시간을 기준으로 데이터가 분할되어, 다중 레벨 파티셔닝 체계가 형성되는 경우가 많습니다. 예를 들어, 매시간 데이터를 수집하는 고객은 연, 월, 일, 시를 기준으로 분할할 수 있습니다. 다양한 소스로부터 데이터를 수집하되 하루에 한 번만 로드하는 고객의 경우 데이터 원본 식별자 및 날짜별로 분할할 수 있습니다.

Athena는 Apache Hive 스타일 파티션을 사용할 수 있습니다. 이 파티션의 데이터 경로에 등호로 연결된 키 값 페어가 포함되어 있습니다(예: `country=us/...` 또는 `year=2021/month=01/day=26/...`). 따라서 경로에는 파티션 키의 이름과 각 경로가 나타내는 값이 모두 포함됩니다. 파티션을 나눈 테이블에 새 Hive 파티션을 로드하려면 [MSCK REPAIR TABLE](msck-repair-table.md) 명령을 사용할 수 있으며 이 명령은 Hive 스타일 파티션에서만 작동합니다.

Athena는 비 Hive 스타일 파티션 체계를 사용할 수도 있습니다. 예를 들어 CloudTrail 로그 및 Firehose 전송 스트림은 `data/2021/01/26/us/6fc7845e.json`와 같은 날짜 부분에 대해 별도의 경로 구성 요소를 사용합니다. Hive와 호환되지 않는 데이터의 경우 [ALTER TABLE ADD PARTITION](alter-table-add-partition.md)을(를) 사용하여 파티션을 수동으로 추가합니다.

## 고려 사항 및 제한 사항
<a name="partitions-considerations-limitations"></a>

파티셔닝을 사용할 때는 다음 사항에 유의하세요.
+ `WHERE` 절에서 분할된 테이블을 쿼리하고 파티션을 지정하면 Athena는 해당 파티션의 데이터만 검색합니다.
+ 객체가 많은 Amazon S3 버킷에 대해 쿼리를 실행하는데 데이터가 분할되지 않은 경우, 그러한 쿼리는 Amazon S3의 `GET` 요청 빈도 제한에 영향을 주고 Amazon S3 예외를 발생시킬 수 있습니다. 오류를 방지하려면 데이터를 분할하세요. 또한 Amazon S3 요청 빈도를 조정합니다. 자세한 내용은 [모범 사례 설계 패턴: Amazon S3 성능 최적화](https://docs.aws.amazon.com/AmazonS3/latest/userguide/request-rate-perf-considerations.html)를 참조하세요.
+ Athena에 사용될 파티션 위치는 `s3` 프로토콜(예: `s3://amzn-s3-demo-bucket/folder/`)을 사용해야 합니다. Athena에서, 다른 프로토콜(예: `s3a://amzn-s3-demo-bucket/folder/`)을 사용하는 위치는 포함 테이블에서 `MSCK REPAIR TABLE` 쿼리를 실행할 때 쿼리 실패를 초래하게 됩니다.
+ Amazon S3 경로가 camel 표기 대신 소문자인지 확인합니다(예: `userId` 대신 `userid`). S3 경로가 camel 표기인 경우 `MSCK REPAIR TABLE`은 AWS Glue Data Catalog에 파티션을 추가하지 않습니다. 자세한 내용은 [MSCK REPAIR TABLE](msck-repair-table.md) 섹션을 참조하세요.
+ `MSCK REPAIR TABLE`은 일치하는 파티션 스키마를 찾기 위해 폴더와 하위 폴더를 모두 스캔하기 때문에 별도의 폴더 계층 구조에 있는 별도의 테이블에 데이터를 보관해야 합니다. 예를 들어 테이블 1의 데이터를 `s3://amzn-s3-demo-bucket1`에 두고 테이블 2에 대한 데이터을 `s3://amzn-s3-demo-bucket1/table-2-data`에 두었다고 가정합시다. 두 테이블이 모두 문자열로 분할된 경우 `MSCK REPAIR TABLE`은 테이블 2의 파티션을 테이블 1에 추가합니다. 이를 방지하려면 대신에 `s3://amzn-s3-demo-bucket1` 및 `s3://amzn-s3-demo-bucket2`와 같은 별도의 폴더 구조를 사용하세요. 이 동작은 Amazon EMR과 Apache Hive에서도 동일합니다.
+ Athena와 함께 AWS Glue Data Catalog를 사용하는 경우 계정 및 테이블당 파티션의 서비스 할당량은 [AWS Glue 엔드포인트 및 할당량](https://docs.aws.amazon.com/general/latest/gr/glue.html)을 참조하세요.
  + Athena는 1천만 개의 파티션이 있는 AWS Glue 테이블에 대한 쿼리를 지원하지만, 단일 스캔으로 1백만 개 이상의 파티션을 읽을 수는 없습니다. 이러한 시나리오에서는 파티션 인덱싱이 유용할 수 있습니다. 자세한 내용을 알아보려면 AWS Big Data Blog(빅 데이터 블로그) 문서인 [Improve Amazon Athena query performance using AWS Glue Data Catalog partition indexes](https://aws.amazon.com/blogs/big-data/improve-amazon-athena-query-performance-using-aws-glue-data-catalog-partition-indexes/)(파티션 인덱스를 사용하여 Amazon Athena 쿼리 성능 향상)를 참조하세요.
+ AWS Glue Data Catalog를 사용하는 경우 파티션 할당량 증가를 요청하려면 [AWS Glue의 Service Quotas 콘솔](https://console.aws.amazon.com/servicequotas/home?region=us-east-1#!/services/glue/quotas)을 방문하세요.

## 분할된 데이터로 테이블 생성 및 로드
<a name="partitions-creating-loading"></a>

파티션을 사용하는 테이블을 생성하려면 [CREATE TABLE](create-table.md) 문에 `PARTITIONED BY` 절을 사용합니다. `PARTITIONED BY` 절은 다음 예와 같이 데이터를 분할하는 키를 정의합니다. `LOCATION` 절은 분할된 데이터의 루트 위치를 지정합니다.

```
CREATE EXTERNAL TABLE users (
first string,
last string,
username string
)
PARTITIONED BY (id string)
STORED AS parquet
LOCATION 's3://amzn-s3-demo-bucket'
```

테이블을 생성한 후 쿼리를 위해 파티션에 데이터를 로드합니다. Hive 스타일 파티션의 경우, [MSCK REPAIR TABLE](msck-repair-table.md)을(를) 실행합니다. Hive 스타일이 아닌 파티션의 경우, [ALTER TABLE ADD PARTITION](alter-table-add-partition.md)을(를) 사용하여 파티션을 수동으로 추가합니다.

## 쿼리를 위해 Hive 스타일 및 비 Hive 스타일 데이터 준비
<a name="partitions-preparing-data"></a>

다음 섹션에서는 Athena에서 쿼리하기 위해 Hive 스타일 및 비 Hive 스타일 데이터를 준비하는 방법을 보여줍니다.

### 시나리오 1: Hive 형식으로 Amazon S3에 저장된 데이터
<a name="scenario-1-data-already-partitioned-and-stored-on-s3-in-hive-format"></a>

이 시나리오에서는 Amazon S3에 있는 별도의 폴더에 파티션이 저장됩니다. 예를 들어 다음은 지정된 접두사 아래에 S3 객체를 나열하는 [https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/ls.html](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/ls.html) 명령으로 출력된 샘플 광고 노출에 대한 부분 목록입니다.

```
aws s3 ls s3://elasticmapreduce/samples/hive-ads/tables/impressions/

    PRE dt=2009-04-12-13-00/
    PRE dt=2009-04-12-13-05/
    PRE dt=2009-04-12-13-10/
    PRE dt=2009-04-12-13-15/
    PRE dt=2009-04-12-13-20/
    PRE dt=2009-04-12-14-00/
    PRE dt=2009-04-12-14-05/
    PRE dt=2009-04-12-14-10/
    PRE dt=2009-04-12-14-15/
    PRE dt=2009-04-12-14-20/
    PRE dt=2009-04-12-15-00/
    PRE dt=2009-04-12-15-05/
```

여기서 로그는 날짜, 시간 및 분 단위로 설정된 열 이름(dt)으로 저장됩니다. 분할된 열의 상위 폴더 위치, 스키마 및 이름을 DDL에 포함하면 Athena가 해당 하위 폴더의 데이터를 쿼리할 수 있습니다.

#### 테이블 생성
<a name="creating-a-table"></a>

이 데이터로 테이블을 생성하려면 다음 Athena DDL 문과 같이 'dt'를 따라 파티션을 생성합니다.

```
CREATE EXTERNAL TABLE impressions (
    requestBeginTime string,
    adId string,
    impressionId string,
    referrer string,
    userAgent string,
    userCookie string,
    ip string,
    number string,
    processId string,
    browserCookie string,
    requestEndTime string,
    timers struct<modelLookup:string, requestTime:string>,
    threadId string,
    hostname string,
    sessionId string)
PARTITIONED BY (dt string)
ROW FORMAT  serde 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://elasticmapreduce/samples/hive-ads/tables/impressions/' ;
```

이 테이블은 Hive의 기본 JSON serializer-deserializer를 사용하여 Amazon S3에 저장된 JSON 데이터를 읽습니다. 지원되는 형식에 대한 자세한 내용은 [데이터에 적합한 SerDe 선택](supported-serdes.md) 단원을 참조하세요.

#### MSCK REPAIR TABLE 실행
<a name="run-msck-repair-table"></a>

`CREATE TABLE` 쿼리 실행 후 Athena 쿼리 편집기에서 `MSCK REPAIR TABLE` 명령을 실행하여 다음 예와 같이 파티션을 로드합니다.

```
MSCK REPAIR TABLE impressions
```

이 명령을 실행하면 데이터를 쿼리할 준비가 됩니다.

#### 데이터 쿼리
<a name="query-the-data"></a>

파티션 열을 사용하여 노출 테이블의 데이터를 쿼리합니다. 다음은 그 예입니다.

```
SELECT dt,impressionid FROM impressions WHERE dt<'2009-04-12-14-00' and dt>='2009-04-12-13-00' ORDER BY dt DESC LIMIT 100
```

이 쿼리는 다음과 비슷한 결과를 표시합니다.

```
2009-04-12-13-20    ap3HcVKAWfXtgIPu6WpuUfAfL0DQEc
2009-04-12-13-20    17uchtodoS9kdeQP1x0XThKl5IuRsV
2009-04-12-13-20    JOUf1SCtRwviGw8sVcghqE5h0nkgtp
2009-04-12-13-20    NQ2XP0J0dvVbCXJ0pb4XvqJ5A4QxxH
2009-04-12-13-20    fFAItiBMsgqro9kRdIwbeX60SROaxr
2009-04-12-13-20    V4og4R9W6G3QjHHwF7gI1cSqig5D1G
2009-04-12-13-20    hPEPtBwk45msmwWTxPVVo1kVu4v11b
2009-04-12-13-20    v0SkfxegheD90gp31UCr6FplnKpx6i
2009-04-12-13-20    1iD9odVgOIi4QWkwHMcOhmwTkWDKfj
2009-04-12-13-20    b31tJiIA25CK8eDHQrHnbcknfSndUk
```

### 시나리오 2: 데이터가 Hive 형식으로 분할되지 않음
<a name="scenario-2-data-is-not-partitioned"></a>

다음 예에서 `aws s3 ls` 명령은 Amazon S3에 저장된 [ELB](elasticloadbalancer-classic-logs.md) 로그를 보여줍니다. 데이터 레이아웃이 `key=value` 페어를 사용하지 않으므로 Hive 형식이 아니라는 점에 유의하세요. (`aws s3 ls` 명령에 대한 `--recursive` 옵션은 지정된 디렉터리 또는 접두사 아래의 모든 파일 또는 객체를 나열하도록 지정합니다.)

```
aws s3 ls s3://athena-examples-myregion/elb/plaintext/ --recursive

2016-11-23 17:54:46   11789573 elb/plaintext/2015/01/01/part-r-00000-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:46    8776899 elb/plaintext/2015/01/01/part-r-00001-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:46    9309800 elb/plaintext/2015/01/01/part-r-00002-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47    9412570 elb/plaintext/2015/01/01/part-r-00003-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47   10725938 elb/plaintext/2015/01/01/part-r-00004-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:46    9439710 elb/plaintext/2015/01/01/part-r-00005-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47          0 elb/plaintext/2015/01/01_$folder$
2016-11-23 17:54:47    9012723 elb/plaintext/2015/01/02/part-r-00006-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47    7571816 elb/plaintext/2015/01/02/part-r-00007-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:47    9673393 elb/plaintext/2015/01/02/part-r-00008-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48   11979218 elb/plaintext/2015/01/02/part-r-00009-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48    9546833 elb/plaintext/2015/01/02/part-r-00010-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48   10960865 elb/plaintext/2015/01/02/part-r-00011-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48          0 elb/plaintext/2015/01/02_$folder$
2016-11-23 17:54:48   11360522 elb/plaintext/2015/01/03/part-r-00012-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48   11211291 elb/plaintext/2015/01/03/part-r-00013-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:48    8633768 elb/plaintext/2015/01/03/part-r-00014-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:49   11891626 elb/plaintext/2015/01/03/part-r-00015-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:49    9173813 elb/plaintext/2015/01/03/part-r-00016-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:49   11899582 elb/plaintext/2015/01/03/part-r-00017-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:49          0 elb/plaintext/2015/01/03_$folder$
2016-11-23 17:54:50    8612843 elb/plaintext/2015/01/04/part-r-00018-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:50   10731284 elb/plaintext/2015/01/04/part-r-00019-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:50    9984735 elb/plaintext/2015/01/04/part-r-00020-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:50    9290089 elb/plaintext/2015/01/04/part-r-00021-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:50    7896339 elb/plaintext/2015/01/04/part-r-00022-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    8321364 elb/plaintext/2015/01/04/part-r-00023-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51          0 elb/plaintext/2015/01/04_$folder$
2016-11-23 17:54:51    7641062 elb/plaintext/2015/01/05/part-r-00024-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51   10253377 elb/plaintext/2015/01/05/part-r-00025-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    8502765 elb/plaintext/2015/01/05/part-r-00026-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51   11518464 elb/plaintext/2015/01/05/part-r-00027-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    7945189 elb/plaintext/2015/01/05/part-r-00028-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    7864475 elb/plaintext/2015/01/05/part-r-00029-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51          0 elb/plaintext/2015/01/05_$folder$
2016-11-23 17:54:51   11342140 elb/plaintext/2015/01/06/part-r-00030-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:51    8063755 elb/plaintext/2015/01/06/part-r-00031-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52    9387508 elb/plaintext/2015/01/06/part-r-00032-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52    9732343 elb/plaintext/2015/01/06/part-r-00033-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52   11510326 elb/plaintext/2015/01/06/part-r-00034-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52    9148117 elb/plaintext/2015/01/06/part-r-00035-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52          0 elb/plaintext/2015/01/06_$folder$
2016-11-23 17:54:52    8402024 elb/plaintext/2015/01/07/part-r-00036-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52    8282860 elb/plaintext/2015/01/07/part-r-00037-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:52   11575283 elb/plaintext/2015/01/07/part-r-00038-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:53    8149059 elb/plaintext/2015/01/07/part-r-00039-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:53   10037269 elb/plaintext/2015/01/07/part-r-00040-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:53   10019678 elb/plaintext/2015/01/07/part-r-00041-ce65fca5-d6c6-40e6-b1f9-190cc4f93814.txt
2016-11-23 17:54:53          0 elb/plaintext/2015/01/07_$folder$
2016-11-23 17:54:53          0 elb/plaintext/2015/01_$folder$
2016-11-23 17:54:53          0 elb/plaintext/2015_$folder$
```

#### ALTER TABLE ADD PARTITION 실행
<a name="run-alter-table-add-partition"></a>

데이터가 Hive 형식이 아니므로 `MSCK REPAIR TABLE` 명령을 사용하여 테이블을 생성한 후 테이블에 파티션을 추가합니다. 그 대신 [ALTER TABLE ADD PARTITION](alter-table-add-partition.md) 명령을 사용하여 각 파티션을 수동으로 추가할 수 있습니다. 예를 들어 s3://athena-examples-*myregion*/elb/plaintext/2015/01/01/에 데이터를 로드하려면 다음 쿼리를 실행합니다. 각 Amazon S3 폴더에 대해 별도의 파티션 열이 필요하지 않으며, 파티션 키 값이 Amazon S3 키와 다를 수 있습니다.

```
ALTER TABLE elb_logs_raw_native_part ADD PARTITION (dt='2015-01-01') location 's3://athena-examples-us-west-1/elb/plaintext/2015/01/01/'
```

파티션이 이미 있는 경우 오류(파티션이 이미 있습니다.)가 수신됩니다. 이 오류를 방지하기 위해 `IF NOT EXISTS` 절을 사용할 수 있습니다. 자세한 내용은 [ALTER TABLE ADD PARTITION](alter-table-add-partition.md) 섹션을 참조하세요. 파티션을 제거하려면 [ALTER TABLE DROP PARTITION](alter-table-drop-partition.md)을 사용합니다.

## 파티션 프로젝션 고려
<a name="partitions-partition-projection"></a>

파티션을 직접 관리하지 않으려면 파티션 프로젝션을 사용합니다. 파티션 프로젝션은 구조가 미리 알려진 고도로 분할된 테이블에 대한 옵션입니다. 파티션 프로젝션에서 파티션 값 및 위치는 메타데이터 리포지토리에서 읽지 않고 구성하는 테이블 속성에서 계산됩니다. 인메모리 계산은 원격 조회보다 빠르기 때문에 파티션 프로젝션을 사용하면 쿼리 런타임을 크게 줄일 수 있습니다.

자세한 내용은 [Amazon Athena에서 파티션 프로젝션 사용](partition-projection.md) 섹션을 참조하세요.

## 추가 리소스
<a name="partitions-additional-resources"></a>
+ Firehose 데이터의 분할 옵션에 대한 자세한 내용은 [Amazon Data Firehose 예제](partition-projection-kinesis-firehose-example.md) 섹션을 참조하세요.
+ [JDBC 드라이버](connect-with-jdbc.md)를 사용하여 파티션 추가를 자동화할 수도 있습니다.
+ CTAS 및 INSERT INTO를 사용하여 데이터 세트를 분할할 수 있습니다. 자세한 내용은 [ETL 및 데이터 분석에 CTAS 및 INSERT INTO 사용](ctas-insert-into-etl.md) 섹션을 참조하세요.

# Amazon Athena에서 파티션 프로젝션 사용
<a name="partition-projection"></a>

Athena에서 파티션 프로젝션을 사용하여 고도로 분할된 테이블의 쿼리 처리 속도를 높이고 파티션 관리를 자동화할 수 있습니다.

파티션 프로젝션에서 Athena는 AWS Glue에서 테이블에 직접 구성하는 테이블 속성에서 파티션 값 및 위치를 계산합니다. 테이블 속성을 사용하면 Athena는 필요한 파티션 정보를 '프로젝션'하거나 결정할 수 있으므로 AWS Glue Data Catalog에서 시간이 많이 걸리는 메타데이터를 검색하지 않아도 됩니다. 메모리 내 작업은 주로 원격 작업보다 빠르기 때문에 파티션 프로젝션은 고도로 분할된 테이블에 대한 쿼리의 실행 시간을 줄일 수 있습니다. 쿼리 및 기본 데이터의 특정 특성에 따라 파티션 프로젝션은 파티션 메타데이터 검색에 제한되는 쿼리에 대한 쿼리 실행 시간을 크게 줄일 수 있습니다.

## 파티션 정리 및 파티션 프로젝션 이해
<a name="partition-projection-pruning-vs-projection"></a>

파티션 정리는 메타데이터를 수집하여 쿼리에 적용되는 파티션으로만 해당 메타데이터를 “정리”합니다. 이렇게 하면 종종 쿼리 속도가 높아집니다. Athena는 파티션 프로젝션용으로 구성된 테이블을 포함하여 파티션 열이 있는 모든 테이블에 대해 파티션 정리를 사용합니다.

일반적으로 쿼리를 처리할 때 Athena는 파티션 정리를 수행하기 전에 AWS Glue Data Catalog에 대한 `GetPartitions` 호출을 수행합니다. 테이블에 많은 수의 파티션이 있는 경우 `GetPartitions`를 사용하면 성능에 부정적인 영향을 줄 수 있습니다. 이를 방지하기 위해 파티션 프로젝션을 사용할 수 있습니다. 파티션 프로젝션을 사용하면 파티션 프로젝션 구성이 파티션 자체를 빌드하는 데 필요한 모든 정보를 Athena에 제공하므로 Athena는 `GetPartitions`를 호출하지 않아도 됩니다.

## 파티션 프로젝션을 사용하는 방법
<a name="partition-projection-using"></a>

파티션 프로젝션을 사용하려면 AWS Glue Data Catalog 또는 [외부 Hive 메타스토어](connect-to-data-source-hive.md)의 테이블 속성에서 각 파티션 열에 대한 파티션 값 및 프로젝션 형식의 범위를 지정합니다. 테이블의 이러한 사용자 지정 속성을 사용하면 Athena가 테이블에서 쿼리를 실행할 때 예상되는 파티션 패턴을 알 수 있습니다. 쿼리를 실행하는 동안 Athena는 AWS Glue Data Catalog 또는 외부 Hive 메타스토어에서 파티션 값을 검색하는 대신 이 정보를 사용하여 파티션 값을 프로젝션합니다. 이렇게 하면 쿼리 실행 시간이 단축될 뿐만 아니라 Athena, AWS Glue 또는 외부 Hive 메타스토어에 파티션을 수동으로 만들 필요가 없으므로 파티션 관리도 자동화할 수 있습니다.

**중요**  
테이블에서 파티션 프로젝션을 활성화하면 Athena는 AWS Glue Data Catalog 또는 Hive 메타스토어의 테이블에 등록된 모든 파티션 메타데이터를 무시합니다.

## 일부 사용 사례
<a name="partition-projection-use-cases"></a>

파티션 프로젝션이 유용한 시나리오는 다음과 같습니다.
+ 고도로 분할된 테이블에 대한 쿼리는 원하는 만큼 빨리 완료되지 않습니다.
+ 데이터에 새 날짜 또는 시간 파티션이 생성될 때 정기적으로 테이블에 파티션을 추가합니다. 파티션 프로젝션을 사용하면 새 데이터가 도착할 때 사용할 수 있는 상대 날짜 범위를 구성할 수 있습니다.
+ Amazon S3에 고도로 분할된 데이터가 있습니다. AWS Glue Data Catalog 또는 Hive 메타스토어에서 데이터를 모델링하는 것은 실용적이지 않으며 쿼리는 데이터의 작은 부분만 읽습니다.

### 프로젝션 가능한 파티션 구조
<a name="partition-projection-known-data-structures"></a>

파티션 프로젝션은 파티션이 다음과 같은(이에 국한되지 않음) 예측 가능한 패턴을 따를 때 가장 쉽게 구성할 수 있습니다.
+ **정수** – `[1, 2, 3, 4, ..., 1000]` 또는 `[0500, 0550, 0600, ..., 2500]`과 같은 정수의 연속 시퀀스입니다.
+ **날짜** – `[20200101, 20200102, ..., 20201231]` 또는 `[1-1-2020 00:00:00, 1-1-2020 01:00:00, ..., 12-31-2020 23:00:00]`과 같은 날짜 또는 날짜/시간의 연속 시퀀스입니다.
+ **열거형 값** - 공항 코드 또는 AWS 리전과 같은 열거형 값의 유한 집합입니다.
+ **AWS 서비스 로그** - AWS 서비스 로그에는 일반적으로 AWS Glue에서 지정할 수 있고 Athena가 파티션 프로젝션에 사용할 수 있는 파티션 체계를 가진 알려진 구조가 있습니다.

### 파티션 경로 템플릿을 사용자 지정하는 방법
<a name="partition-projection-custom-s3-storage-locations"></a>

기본적으로 Athena는 `s3://amzn-s3-demo-bucket/<table-root>/partition-col-1=<partition-col-1-val>/partition-col-2=<partition-col-2-val>/` 양식을 사용하여 파티션 위치를 작성하지만 데이터가 다르게 구성된 경우 Athena는 이 경로 템플릿을 사용자 지정할 수 있는 메커니즘을 제공합니다. 단계는 [사용자 지정 S3 스토리지 위치를 지정하는 방법](partition-projection-setting-up.md#partition-projection-specifying-custom-s3-storage-locations)를 참조하세요.

## 고려 사항 및 제한 사항
<a name="partition-projection-considerations-and-limitations"></a>

다음 사항을 고려하세요.
+ 파티션 프로젝션을 사용할 경우 AWS Glue 또는 외부 Hive 메타스토어에서 수동으로 파티션을 지정할 필요가 없습니다.
+ 테이블에서 파티션 프로젝션을 활성화하면 Athena는 해당 테이블에 대한 AWS Glue Data Catalog 또는 외부 Hive 메타스토어의 파티션 메타데이터를 무시합니다.
+ Amazon S3에 프로젝션된 파티션이 없는 경우 Athena는 계속해서 파티션을 프로젝션합니다. Athena가 오류를 발생시키지는 않지만 데이터가 반환되지 않습니다. 그러나 너무 많은 파티션이 비어 있으면 기존 AWS Glue 파티션에 비해 성능이 느려질 수 있습니다. 프로젝션된 파티션의 절반 이상이 비어 있으면 기존 파티션을 사용하는 것이 좋습니다.
+ 파티션 프로젝션에 대해 정의된 범위 한계를 벗어난 값에 대한 쿼리는 오류를 반환하지 않습니다. 그 대신 쿼리가 실행되지만 0개의 행을 반환합니다. 예를 들어 2,020으로 시작하고 `'projection.timestamp.range'='2020/01/01,NOW'`로 정의된 시간 관련 데이터가 있는 경우 `SELECT * FROM table-name WHERE timestamp = '2019/02/02'` 같은 쿼리는 성공적으로 완료되지만 0개의 행을 반환합니다.
+ Athena를 통해 테이블을 쿼리할 때만 파티션 프로젝션을 사용할 수 있습니다. Amazon Redshift Spectrum, Athena for Spark, Amazon EMR 등의 다른 서비스를 통해 동일한 테이블을 읽는 경우 표준 파티션 메타데이터가 사용됩니다.
+ 파티션 프로젝션은 DML 전용 기능이므로 `SHOW PARTITIONS`에서는 Athena에 의해 프로젝션되었지만 AWS Glue 카탈로그 또는 외부 Hive 메타스토어에 등록되지 않은 파티션은 나열하지 않습니다.
+ Athena는 파티션 프로젝션을 위한 구성으로 뷰의 테이블 속성을 사용하지 않습니다. 이 제한을 해결하려면 뷰가 참조하는 테이블의 테이블 속성에서 파티션 프로젝션을 구성하고 활성화합니다.

## 비디오
<a name="partition-projection-video"></a>

다음 비디오는 Athena에서 쿼리의 성능을 향상시키기 위해 파티션 프로젝션을 사용하는 방법을 보여줍니다.

[![AWS Videos](http://img.youtube.com/vi/https://www.youtube.com/embed/iUD5pPpcyZk/0.jpg)](http://www.youtube.com/watch?v=https://www.youtube.com/embed/iUD5pPpcyZk)


**Topics**
+ [파티션 정리 및 파티션 프로젝션 이해](#partition-projection-pruning-vs-projection)
+ [파티션 프로젝션을 사용하는 방법](#partition-projection-using)
+ [일부 사용 사례](#partition-projection-use-cases)
+ [고려 사항 및 제한 사항](#partition-projection-considerations-and-limitations)
+ [비디오](#partition-projection-video)
+ [파티션 프로젝션 설정](partition-projection-setting-up.md)
+ [파티션 프로젝션에 지원되는 형식](partition-projection-supported-types.md)
+ [동적 ID 분할 사용](partition-projection-dynamic-id-partitioning.md)
+ [Amazon Data Firehose 예제](partition-projection-kinesis-firehose-example.md)

# 파티션 프로젝션 설정
<a name="partition-projection-setting-up"></a>

테이블의 속성에서 파티션 프로젝션을 설정하는 과정은 두 단계로 이루어집니다.

1. 각 파티션 열에 대한 데이터 범위 및 관련 패턴을 지정하거나 사용자 지정 템플릿을 사용합니다.

1. 테이블에 대해 파티션 프로젝션을 활성화합니다.

**참고**  
기존 테이블에 파티션 프로젝션 속성을 추가하기 전에 파티션 프로젝션 속성을 설정하려는 파티션 열이 테이블 스키마에 이미 있어야 합니다. 파티션 열이 아직 없는 경우 기존 테이블에 파티션 열을 수동으로 추가해야 합니다. AWS Glue에서는 이 단계를 자동으로 수행하지 않습니다.

이 단원에서는 AWS Glue에 대해 테이블 속성을 설정하는 방법을 보여줍니다. 이러한 속성을 설정하기 위해 AWS Glue 콘솔, Athena [CREATE TABLE](create-table.md) 쿼리 또는 [AWS Glue API](https://docs.aws.amazon.com/glue/latest/dg/aws-glue-api.html) 작업을 사용할 수 있습니다. 다음 절차에서는 AWS Glue 콘솔에서 속성을 설정하는 방법을 보여줍니다.

**AWS Glue 콘솔을 사용하여 파티션 프로젝션을 구성하고 활성화하려면**

1. AWS Management Console에 로그인하여 [https://console.aws.amazon.com/glue/](https://console.aws.amazon.com/glue/)에서 AWS Glue 콘솔을 엽니다.

1. **테이블** 탭을 선택합니다.

   **테이블** 탭에서 기존 테이블을 편집하거나 **테이블 추가**를 선택하여 새 테이블을 생성할 수 있습니다. 수동으로 또는 크롤러를 사용하여 테이블을 추가하는 방법에 대한 자세한 내용은 *AWS Glue 개발자 안내서*의 [AWS Glue 콘솔에서 테이블 관련 작업](https://docs.aws.amazon.com/glue/latest/dg/console-tables.html)을 참조하세요.

1. 테이블 목록에서 편집하려는 테이블에 대한 링크를 선택합니다.  
![\[AWS Glue 콘솔에서 편집할 테이블을 선택합니다.\]](http://docs.aws.amazon.com/ko_kr/athena/latest/ug/images/partition-projection-1.png)

1. **Actions**(작업), **Edit table**(테이블 편집)을 선택합니다.

1. **Edit table**(테이블 편집) 페이지의 **Table properties**(테이블 속성) 섹션에서 분할된 각 열에 대해 다음 키-값 페어를 추가합니다.

   1. **키**에 `projection.columnName.type`을 추가합니다.

   1. **값**에 지원되는 형식 `enum`, `integer`, `date`, `injected` 중 하나를 추가합니다. 자세한 내용은 [파티션 프로젝션에 지원되는 형식](partition-projection-supported-types.md) 단원을 참조하세요.

1. [파티션 프로젝션에 지원되는 형식](partition-projection-supported-types.md)의 지침을 따르고, 구성 요구 사항에 따라 추가적인 키-값 페어를 추가합니다.

   다음 예제 테이블 구성은 파티션 프로젝션에 대한 `year` 열을 구성하여 반환할 수 있는 값을 2010에서 2016까지의 범위로 제한합니다.  
![\[AWS Glue 콘솔 테이블 속성에서 파티션 열에 대한 파티션 프로젝션 구성.\]](http://docs.aws.amazon.com/ko_kr/athena/latest/ug/images/partition-projection-3.png)

1. 키-값 페어를 추가하여 파티션 프로젝션을 활성화합니다. **키**에 `projection.enabled`를 입력하고 **값**에 `true`를 입력합니다.
**참고**  
`projection.enabled`을 `false`로 설정하여 언제든지 이 테이블에서 파티션 프로젝션을 비활성화할 수 있습니다.

1. 작업을 마쳤으면 **저장**을 선택합니다.

1. Athena 쿼리 편집기에서 테이블에 대해 구성한 열을 테스트 쿼리합니다.

   다음 예제 쿼리는 `SELECT DISTINCT`를 사용하여 `year` 열에서 고유한 값을 반환합니다. 데이터베이스에는 1987년부터 2016년까지 데이터가 포함되어 있지만 `projection.year.range` 속성은 반환되는 값을 2010년에서 2016년까지로 제한합니다.  
![\[파티션 프로젝션을 사용하는 열 쿼리.\]](http://docs.aws.amazon.com/ko_kr/athena/latest/ug/images/partition-projection-5.png)
**참고**  
`projection.enabled`를 `true`로 설정했지만 하나 이상의 파티션 열을 구성하지 못하면 다음과 같은 오류 메시지가 나타납니다.  
`HIVE_METASTORE_ERROR: Table database_name.table_name is configured for partition projection, but the following partition columns are missing projection configuration: [column_name] (table database_name.table_name)`.

## 사용자 지정 S3 스토리지 위치를 지정하는 방법
<a name="partition-projection-specifying-custom-s3-storage-locations"></a>

AWS Glue에서 테이블 속성을 편집할 때 프로젝션된 파티션에 대한 사용자 지정 Amazon S3 경로 템플릿을 지정할 수도 있습니다. 사용자 지정 템플릿을 사용하면 Athena가 일반적인 `.../column=value/...` 패턴을 따르지 않는 사용자 지정 Amazon S3 파일 위치에 파티션 값을 올바르게 매핑할 수 있습니다.

사용자 지정 템플릿을 사용하는 것은 선택 사항입니다. 그러나 사용자 지정 템플릿을 사용하는 경우 템플릿에 각 파티션 열에 대한 자리 표시자를 포함시켜야 합니다. 분할된 데이터 파일이 파티션별 ‘폴더’에 상주하도록 템플릿 지정된 위치는 슬래시로 끝나야 합니다.

**사용자 지정 파티션 위치 템플릿을 지정하려면**

1. [AWS Glue 콘솔을 사용하여 파티션 프로젝션을 구성하고 활성화](#partition-projection-setting-up-procedure)하는 단계에 따라 다음과 같이 사용자 지정 템플릿을 지정하는 키-값 페어를 추가합니다.

   1. **키**에 `storage.location.template`를 입력합니다.

   1. **값**에서 모든 파티션 열에 대한 자리 표시자를 포함하는 위치를 지정합니다. 각 자리 표시자(및 S3 경로 자체)가 단일 슬래시로 종료되었는지 확인합니다.

      다음 예제 템플릿 값은 테이블에 파티션 열 `a`, `b` 및 `c`가 있다고 가정합니다.

      ```
      s3://amzn-s3-demo-bucket/table_root/a=${a}/${b}/some_static_subdirectory/${c}/      
      ```

      ```
      s3://amzn-s3-demo-bucket/table_root/c=${c}/${b}/some_static_subdirectory/${a}/${b}/${c}/${c}/      
      ```

      같은 테이블의 경우 다음 예제 템플릿 값은 열 `c`에 대한 자리 표시자가 포함되어 있지 않기 때문에 유효하지 않습니다.

      ```
      s3://amzn-s3-demo-bucket/table_root/a=${a}/${b}/some_static_subdirectory/         
      ```

1. **Apply(적용)**를 선택합니다.

# 파티션 프로젝션에 지원되는 형식
<a name="partition-projection-supported-types"></a>

테이블에는 `enum`, `integer`, `date,` 또는 `injected` 파티션 열 형식의 조합이 있을 수 있습니다.

## 열거형 형식
<a name="partition-projection-enum-type"></a>

값이 열거형 집합의 멤버(예: 공항 코드 또는 AWS 리전)인 파티션 열의 `enum` 형식을 사용합니다.

테이블에서 다음과 같이 파티션 속성을 정의합니다.


****  

| 속성 이름 | 예제 값 | 설명 | 
| --- | --- | --- | 
| projection.columnName.type |  `enum`  | 필수 사항입니다. columnName 열에 사용할 프로젝션 형식입니다. 열거형 형식의 사용을 나타내기 위해서는 값이 enum이어야 합니다(대소문자 구분 안 함). 선행 및 후행 공백을 사용할 수 있습니다. | 
| projection.columnName.values |  `A,B,C,D,E,F,G,Unknown`  | 필수 사항입니다. ColumnName 열에 대한 열거형 파티션 값의 쉼표로 구분된 목록입니다. 공백은 열거형 값의 일부로 간주됩니다. | 

**참고**  
가장 좋은 방법은 `enum` 기반 파티션 프로젝션 사용을 수십 개 이하로 제한하는 것입니다 `enum` 프로젝션에 대해 구체적인 제한은 없지만 테이블의 메타데이터 총 크기는 gzip 압축 시 약 1MB의 AWS Glue 제한을 초과할 수 없습니다. 이 제한은 열 이름, 위치, 스토리지 형식 등과 같은 테이블의 주요 부분에서 공유됩니다. 자신이 `enum` 프로젝션에 수십 개 이상의 고유 ID를 사용하고 있다면 대리 필드에서 더 적은 수의 고유 값으로 버킷팅하는 등의 대안적 방법을 고려하는 것이 좋습니다. 카디널리티를 포기함으로써 `enum` 필드에서 고유 값의 수를 제어할 수 있습니다.

## 정수 형식
<a name="partition-projection-integer-type"></a>

가능한 값을 정의된 범위 내의 정수로 해석할 수 있는 파티션 열에 대해 정수 형식을 사용합니다. 프로젝션 정수 열은 현재 Java 부호 있는 long(-263\$1263-1 포함)의 범위로 제한됩니다.


****  

| 속성 이름 | 예제 값 | 설명 | 
| --- | --- | --- | 
| projection.columnName.type |  `integer`  | 필수 사항입니다. columnName 열에 사용할 프로젝션 형식입니다. 정수 형식의 사용을 나타내기 위해서는 값이 integer여야 합니다(대소문자 구분 안 함). 선행 및 후행 공백을 사용할 수 있습니다. | 
| projection.columnName.range |  `0,10` `-1,8675309` `0001,9999`  | 필수 사항입니다. columnName 열에 대한 쿼리에서 반환할 최소 및 최대 범위 값을 제공하는 두 요소의 쉼표로 구분된 목록입니다. 값을 하이픈이 아닌 쉼표로 구분해야 합니다. 이 값은 포함되며 음수일 수 있으며 선행 0을 포함할 수 있습니다. 선행 및 후행 공백을 사용할 수 있습니다. | 
| projection.columnName.interval |  `1` `5`  | 선택 사항. columnName 열에 대한 연속 파티션 값 사이의 간격을 지정하는 양의 정수입니다. 예를 들어 interval 값이 “1"인 range 값 “1,3"은 1, 2, 3 값을 생성합니다. interval 값이 “2"인 동일한 range 값은 값 1과 3을 생성하며 2를 건너뜁니다. 선행 및 후행 공백을 사용할 수 있습니다. 기본값은 1입니다. | 
| projection.columnName.digits |  `1` `5`  | 선택 사항. columnName 열에 대한 파티션 값의 최종 표시에 포함할 자릿수를 지정하는 양의 정수입니다. 예를 들어 digits 값이 “1"인 range 값 “1,3"은 1, 2, 3 값을 생성합니다. digits 값이 “2"인 동일한 range 값은 01, 02 및 03 값을 생성합니다. 선행 및 후행 공백을 사용할 수 있습니다. 기본값은 고정 자릿수와 선행 0이 없습니다. | 

## 날짜 형식
<a name="partition-projection-date-type"></a>

정의된 범위 내에서 값을 날짜(선택적 시간 포함)로 해석할 수 있는 파티션 열에 대해 날짜 형식을 사용합니다.

**중요**  
프로젝션 날짜 열은 쿼리 실행 시 UTC(협정 세계 표준시)로 생성됩니다.


****  

| 속성 이름 | 예제 값 | 설명 | 
| --- | --- | --- | 
| projection.columnName.type |  `date`  | 필수 사항입니다. columnName 열에 사용할 프로젝션 형식입니다. 날짜 형식의 사용을 나타내기 위해서는 값이 date여야 합니다(대소문자 구분 안 함). 선행 및 후행 공백을 사용할 수 있습니다. | 
| projection.columnName.range |  `201701,201812` `01-01-2010,12-31-2018` `NOW-3YEARS,NOW` `201801,NOW+1MONTH`  |  필수 사항입니다. *columnName* 열에 대한 최소 및 최대 `range` 값을 제공하는 두 요소의 쉼표로 구분된 목록입니다. 이 값은 포함되며 Java `java.time.*` 날짜 형식과 호환되는 모든 형식을 사용할 수 있습니다. 최소값과 최대값 모두 동일한 형식을 사용해야 합니다. `.format` 특성에 지정된 형식은 이러한 값에 사용되는 형식이어야 합니다. 이 열에는 이 정규식 패턴으로 서식이 지정된 상대 날짜 문자열도 포함될 수 있습니다. `\s*NOW\s*(([\+\-])\s*([0-9]+)\s*(YEARS?\|MONTHS?\|WEEKS?\|DAYS?\|HOURS?\|MINUTES?\|SECONDS?)\s*)?` 공백은 허용되지만 날짜 리터럴은 날짜 문자열 자체의 일부로 간주됩니다.  | 
| projection.columnName.format |  `yyyyMM` `dd-MM-yyyy` `dd-MM-yyyy-HH-mm-ss`  | 필수 사항입니다. Java 날짜 형식 [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)를 기반으로 하는 날짜 형식 문자열입니다. 지원되는 모든 Java.time.\$1 형식일 수 있습니다. | 
| projection.columnName.interval |  `1` `5`  |  *columnName에* 열에 대한 연속 파티션 값 사이의 간격을 지정하는 양의 정수입니다. 예를 들어 `interval` 값이 `1`이고 `interval.unit` 값이 `MONTHS`인 `range`값 `2017-01,2018-12`는 2017-01, 2017-02, 2017-03 등의 값을 생성합니다. `interval` 값이 `2`이고 `interval.unit` 값이 `MONTHS`인 동일한 `range` 값은 2017-01, 2017-03, 2017-05 등의 값을 생성합니다. 선행 및 후행 공백을 사용할 수 있습니다. 제공된 날짜가 하루 또는 한 달 정밀도인 경우 `interval`은 선택 사항이며 기본값은 각각 1일 또는 1개월입니다. 그렇지 않으면 `interval`이 필요합니다.  | 
| projection.columnName.interval.unit |  `YEARS` `MONTHS` `WEEKS` `DAYS` `HOURS` `MINUTES` `SECONDS` `MILLIS`  |  [ChronoUnit](https://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoUnit.html)의 직렬화된 형식을 나타내는 시간 단위 단어입니다. 가능한 값은 `YEARS`, `MONTHS`, `WEEKS`, `DAYS`, `HOURS`, `MINUTES`, `SECONDS` 또는 `MILLIS`입니다. 이러한 값은 대/소문자를 구분합니다. 제공된 날짜가 하루 또는 한 달 정밀도인 경우 `interval.unit`은 선택 사항이며 기본값은 각각 1일 또는 1개월입니다. 그렇지 않으면 `interval.unit`이 필요합니다.  | 

**Example -월별 분할**  
다음 예제 테이블 구성은 2015년부터 현재까지의 데이터를 월별로 분할합니다.  

```
'projection.month.type'='date', 
'projection.month.format'='yyyy-MM', 
'projection.month.interval'='1', 
'projection.month.interval.unit'='MONTHS', 
'projection.month.range'='2015-01,NOW', 
...
```

## 삽입 형식
<a name="partition-projection-injected-type"></a>

일부 논리적 범위 내에서 절차에 따라 생성될 수 없지만 쿼리의 `WHERE` 절에서 단일 값으로 제공되는 가능한 값을 가진 파티션 열에 대해 삽입된 형식을 사용하세요.

다음 사항을 명심해야 합니다.
+ 삽입된 각 열에 대해 필터 식이 제공되지 않으면 삽입된 열에 대한 쿼리가 실패합니다.
+ 주입된 열의 필터 식에 대해 여러 값이 있는 쿼리는 값이 분리된 경우에만 성공합니다.
+ `string` 형식의 열만 지원됩니다.
+ 주입된 파티션 열과 함께 `WHERE IN` 절을 사용하는 경우 `IN` 목록에서 지정할 수 있는 값은 1,000개로 제한됩니다. 주입된 열에 대해 파티션이 1,000개를 초과하는 데이터세트를 쿼리하려면 쿼리를 여러 개의 작은 쿼리로 분할한 후(각각 `WHERE IN` 절에서 최대 1,000개의 값을 보유함) 결과를 집계합니다.


****  

| 속성 이름 | 값 | 설명 | 
| --- | --- | --- | 
| projection.columnName.type |  `injected`  | 필수 사항입니다. columnName 열에 사용할 프로젝션 형식입니다. string 형식만 지원됩니다. 지정된 값은 injected여야 합니다(대소문자 구분 안 함). 선행 및 후행 공백을 사용할 수 있습니다. | 

자세한 내용은 [`injected` 프로젝션 유형을 사용해야 하는 경우](partition-projection-dynamic-id-partitioning.md#partition-projection-injection) 섹션을 참조하세요.

# 동적 ID 분할 사용
<a name="partition-projection-dynamic-id-partitioning"></a>

데이터가 카디널리티가 높은 속성으로 파티션되거나 값을 미리 알 수 없는 경우 `injected` 프로젝션 유형을 사용할 수 있습니다. 이러한 속성의 예로 사용자 이름과 디바이스 또는 제품의 ID가 있습니다. `injected` 프로젝션 유형을 사용하여 파티션 키를 구성하면 Athena는 쿼리 자체의 값을 사용하여 읽을 파티션 세트를 계산합니다.

다음 조건을 충족해야 Athena가 `injected` 프로젝션 유형으로 구성된 파티션 키가 있는 테이블에서 쿼리를 실행할 수 있습니다.
+ 쿼리에는 파티션 키 값이 하나 이상 포함해야 합니다.
+ 값은 데이터를 읽지 않고도 평가할 수 있는 리터럴 또는 표현식이어야 합니다.

이러한 기준 중 하나라도 충족되지 않으면 쿼리가 실패하고 다음 오류가 발생합니다.

CONSTRAINT\$1VIOLATION: 삽입된 프로젝션 파티션 열 *column\$1name*에는 WHERE 절에 하나 이상의 정적 같음 조건만 포함되어야 합니다.

## `injected` 프로젝션 유형을 사용해야 하는 경우
<a name="partition-projection-injection"></a>

IoT 디바이스의 이벤트로 구성되고 디바이스 ID로 파티션된 데이터 세트가 있다고 가정해 보겠습니다. 다음은 이 데이터 세트의 특성입니다.
+ 디바이스 ID는 무작위로 생성됩니다.
+ 새 디바이스는 자주 프로비저닝됩니다.
+ 현재 수십만 개의 디바이스가 있으며 미래에는 수백만 개가 될 것입니다.

기존 메타스토어로는 이 데이터 세트를 관리하기 어렵습니다. 데이터 스토리지와 메타스토어 간에 파티션을 동기화된 상태로 유지하기가 어렵고 쿼리를 계획하는 동안 파티션 필터링이 느려질 수 있습니다. 그러나 파티션 프로젝션을 사용하고 `injected` 프로젝션 유형을 사용하도록 테이블을 구성하면 메타스토어에서 파티션을 관리할 필요가 없고 쿼리에서 파티션 메타데이터를 조회할 필요가 없다는 두 가지 이점이 있습니다.

다음 `CREATE TABLE` 예제에서는 방금 설명한 디바이스 이벤트 데이터 세트에 대한 테이블을 생성합니다. 테이블에서는 injected 프로젝션 유형을 사용합니다.

```
CREATE EXTERNAL TABLE device_events (
  event_time TIMESTAMP,
  data STRING,
  battery_level INT
)
PARTITIONED BY (
  device_id STRING
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
  "projection.enabled" = "true",
  "projection.device_id.type" = "injected",
  "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${device_id}"
)
```

다음 예제 쿼리는 12시간 동안 3개의 특정 디바이스에서 수신한 이벤트 수를 조회합니다.

```
SELECT device_id, COUNT(*) AS events
FROM device_events
WHERE device_id IN (
  '4a770164-0392-4a41-8565-40ed8cec737e',
  'f71d12cf-f01f-4877-875d-128c23cbde17',
  '763421d8-b005-47c3-ba32-cc747ab32f9a'
)
AND event_time BETWEEN TIMESTAMP '2023-11-01 20:00' AND TIMESTAMP '2023-11-02 08:00'
GROUP BY device_id
```

이 쿼리를 실행하면 Athena는 `device_id` 파티션 키에 대한 3개의 값을 확인하고 이들 값을 사용하여 파티션 위치를 계산합니다. Athena는 `storage.location.template` 속성 값을 사용하여 다음 위치를 생성합니다.
+ `s3://amzn-s3-demo-bucket/prefix/4a770164-0392-4a41-8565-40ed8cec737e`
+ `s3://amzn-s3-demo-bucket/prefix/f71d12cf-f01f-4877-875d-128c23cbde17`
+ `s3://amzn-s3-demo-bucket/prefix/763421d8-b005-47c3-ba32-cc747ab32f9a`

파티션 프로젝션 구성에서 `storage.location.template` 속성을 제외하면 Athena는 Hive 스타일 파티셔닝을 사용하여 `LOCATION`의 값(예: `s3://amzn-s3-demo-bucket/prefix/device_id=4a770164-0392-4a41-8565-40ed8cec737e`)을 기준으로 파티션 위치를 예상합니다.

# Amazon Data Firehose 예제
<a name="partition-projection-kinesis-firehose-example"></a>

Firehose를 사용하여 데이터를 Amazon S3로 전송하는 경우 기본 구성은 다음 예제와 같은 키를 사용하여 객체를 작성합니다.

```
s3://amzn-s3-demo-bucket/prefix/yyyy/MM/dd/HH/file.extension
```

새 데이터가 수신될 때 AWS Glue Data Catalog에 추가하는 대신에 쿼리할 때 파티션을 자동으로 찾는 Athena 테이블을 생성하려면 파티션 프로젝션을 사용할 수 있습니다.

다음 `CREATE TABLE` 예제에서는 기본 Firehose 구성을 사용합니다.

```
CREATE EXTERNAL TABLE my_ingested_data (
 ...
)
...
PARTITIONED BY (
 datehour STRING
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.datehour.type" = "date",
 "projection.datehour.format" = "yyyy/MM/dd/HH",
 "projection.datehour.range" = "2021/01/01/00,NOW",
 "projection.datehour.interval" = "1",
 "projection.datehour.interval.unit" = "HOURS",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${datehour}/"
)
```

`CREATE TABLE` 문의 `TBLPROPERTIES` 절은 Athena에 다음을 알려줍니다.
+ 테이블을 쿼리할 때 파티션 프로젝션 사용
+ 파티션 키 `datehour`는 `date` 형식입니다(선택적 시간 포함).
+ 날짜 형식 지정 방법
+ 날짜 시간 범위입니다. 값을 하이픈이 아닌 쉼표로 구분해야 합니다.
+ Amazon S3에 대한 데이터를 찾을 수 있는 위치

테이블을 쿼리할 때 Athena는 `datehour`에 대한 값을 계산하고 스토리지 위치 템플릿을 사용하여 파티션 위치 목록을 생성합니다.

**Topics**
+ [`date` 형식을 사용하는 방법](partition-projection-kinesis-firehose-example-using-the-date-type.md)
+ [파티션 키를 선택하는 방법](partition-projection-kinesis-firehose-example-choosing-partition-keys.md)
+ [사용자 지정 접두사 및 동적 파티셔닝을 사용하는 방법](partition-projection-kinesis-firehose-example-using-custom-prefixes-and-dynamic-partitioning.md)

# `date` 형식을 사용하는 방법
<a name="partition-projection-kinesis-firehose-example-using-the-date-type"></a>

프로젝션된 파티션 키에 대해 `date` 형식을 사용하는 경우 범위를 지정해야 합니다. Firehose 전송 스트림이 생성되기 전의 날짜에 대한 데이터가 없으므로 생성 날짜를 시작으로 사용할 수 있습니다. 향후 날짜에 대한 데이터가 없으므로 특수 토큰 `NOW`를 종료로 사용할 수 있습니다.

`CREATE TABLE` 예에서 시작 날짜는 2021년 1월 1일 UTC 자정으로 지정됩니다.

**참고**  
Athena가 기존 파티션만 찾을 수 있도록 데이터가 최대한 가깝게 일치하는 범위를 구성합니다.

예제 테이블에서 쿼리가 실행되면 Athena는 값을 생성하는 범위와 조합된 `datehour` 파티션 키 조건을 사용합니다. 다음과 같은 쿼리를 가정합니다.

```
SELECT *
FROM my_ingested_data
WHERE datehour >= '2020/12/15/00'
AND datehour < '2021/02/03/15'
```

`SELECT` 쿼리의 첫 번째 조건은 `CREATE TABLE` 문에 의해 지정된 날짜 범위의 시작 이전 날짜를 사용합니다. 파티션 프로젝션 구성에서는 2021년 1월 1일 이전 날짜의 파티션을 지정하지 않으므로 Athena는 다음 위치에서만 데이터를 찾고 쿼리의 이전 날짜는 무시합니다.

```
s3://amzn-s3-demo-bucket/prefix/2021/01/01/00/
s3://amzn-s3-demo-bucket/prefix/2021/01/01/01/
s3://amzn-s3-demo-bucket/prefix/2021/01/01/02/
...
s3://amzn-s3-demo-bucket/prefix/2021/02/03/12/
s3://amzn-s3-demo-bucket/prefix/2021/02/03/13/
s3://amzn-s3-demo-bucket/prefix/2021/02/03/14/
```

마찬가지로 쿼리가 2021년 2월 3일 15:00 이전의 날짜와 시간에 실행된 경우 마지막 파티션은 쿼리 조건의 날짜와 시간이 아니라 현재 날짜와 시간을 반영합니다.

가장 최근 데이터를 쿼리하려는 경우, 다음 예와 같이 Athena가 미래 날짜를 생성하지 않고 시작 `datehour`만 지정한다는 사실을 활용할 수 있습니다.

```
SELECT *
FROM my_ingested_data
WHERE datehour >= '2021/11/09/00'
```

# 파티션 키를 선택하는 방법
<a name="partition-projection-kinesis-firehose-example-choosing-partition-keys"></a>

파티션 프로젝션이 파티션 위치를 파티션 키에 매핑하는 방법을 지정할 수 있습니다. 이전 섹션의 `CREATE TABLE` 예제에서 날짜와 시간은 datehour라는 하나의 파티션 키로 결합되었지만 다른 체계도 가능합니다. 예를 들어 년, 월, 일 및 시간에 대해 별도의 파티션 키를 사용하여 테이블을 구성할 수도 있습니다.

하지만 날짜를 년, 월, 일로 분할하면 `date` 파티션 프로젝션 유형을 사용할 수 없습니다. 또 다른 방법은 시간에서 날짜를 분리하여 `date` 파티션 프로젝션 유형을 계속 활용하면서 시간 범위를 지정하는 쿼리를 더 읽기 쉽게 만드는 것입니다.

이를 염두에 두고 다음 `CREATE TABLE` 예에서는 날짜와 시간을 구분합니다. SQL에서는 `date`가 예약어이기 때문에 이 예제에서는 날짜를 나타내는 파티션 키의 이름으로 `day`를 사용합니다.

```
CREATE EXTERNAL TABLE my_ingested_data2 (
 ...
)
...
PARTITIONED BY (
 day STRING,
 hour INT
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.day.type" = "date",
 "projection.day.format" = "yyyy/MM/dd",
 "projection.day.range" = "2021/01/01,NOW",
 "projection.day.interval" = "1",
 "projection.day.interval.unit" = "DAYS",
 "projection.hour.type" = "integer",
 "projection.hour.range" = "0,23",
 "projection.hour.digits" = "2",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${day}/${hour}/"
)
```

예제 `CREATE TABLE` 문에서 시간은 정수로 구성된 별도의 파티션 키입니다. 시간 파티션 키의 구성은 0 \$1 23의 범위를 지정하고 Athena가 파티션 위치를 생성할 때 시간을 두 자리 숫자로 형식화해야 합니다.

`my_ingested_data2` 테이블에 대한 쿼리는 다음과 같을 수 있습니다.

```
SELECT *
FROM my_ingested_data2
WHERE day = '2021/11/09'
AND hour > 3
```

## 파티션 키 및 파티션 프로젝션 데이터 형식 이해
<a name="partition-projection-kinesis-firehose-example-partition-key-types-and-partition-projection-types"></a>

첫 번째 `CREATE TABLE` 예의 `datehour` 키는 파티션 프로젝션 구성에서 `date`로 구성되지만 파티션 키의 유형은 `string`입니다. 두 번째 예의 `day`도 마찬가지입니다. 파티션 프로젝션 구성의 유형은 파티션 위치를 생성할 때 값의 형식을 지정하는 방법만 Athena에 알려줍니다. 지정한 형식은 파티션 키의 유형을 변경하지 않습니다. 쿼리에서 `datehour` 및 `day`는 `string` 형식입니다.

쿼리에 `day = '2021/11/09'`와 같은 조건이 포함된 경우 Athena는 파티션 프로젝션 구성에 지정된 날짜 형식을 사용하여 표현식의 오른쪽에 있는 문자열을 구문 분석합니다. Athena는 날짜가 구성된 범위 내에 있는지 확인한 후 날짜 형식을 다시 사용하여 저장 위치 템플릿에 날짜를 문자열로 삽입합니다.

마찬가지로 `day > '2021/11/09'`와 같은 쿼리 조건의 경우 Athena는 오른쪽을 구문 분석하고 구성된 범위 내에서 일치하는 모든 날짜 목록을 생성합니다. 그런 다음 날짜 형식을 사용하여 각 날짜를 스토리지 위치 템플릿에 삽입하여 파티션 위치 목록을 생성합니다.

`day > '2021-11-09'` 또는 `day > DATE '2021-11-09'`와 같은 조건을 쓰는 작업은 적용되지 않습니다. 첫 번째 경우에는 날짜 형식이 일치하지 않으며(포워드 슬래시 대신 하이픈에 유의) 두 번째 경우에는 데이터 형식이 일치하지 않습니다.

# 사용자 지정 접두사 및 동적 파티셔닝을 사용하는 방법
<a name="partition-projection-kinesis-firehose-example-using-custom-prefixes-and-dynamic-partitioning"></a>

Firehose는 [사용자 지정 접두사](https://docs.aws.amazon.com/firehose/latest/dev/s3-prefixes.html)와 [동적 파티셔닝](https://docs.aws.amazon.com/firehose/latest/dev/dynamic-partitioning.html)으로 구성할 수 있습니다. 이러한 기능을 사용하여 Amazon S3 키를 구성하고 사용 사례를 더 잘 지원하는 분할 체계를 설정할 수 있습니다. 이러한 분할 체계와 함께 파티션 프로젝션을 사용하여 적절하게 구성할 수도 있습니다.

예를 들어 사용자 지정 접두사 기능을 사용하여 기본 `yyyy/MM/dd/HH` 체계 대신 ISO 형식의 날짜가 있는 Amazon S3 키를 가져올 수 있습니다.

사용자 지정 접두사를 동적 파티셔닝과 결합하여 다음 예제와 같이 Firehose 메시지의 `customer_id`와 같은 속성을 추출할 수 있습니다.

```
prefix/!{timestamp:yyyy}-!{timestamp:MM}-!{timestamp:dd}/!{partitionKeyFromQuery:customer_id}/
```

Amazon S3 접두사를 사용하면 Firehose 전송 스트림에서 `s3://amzn-s3-demo-bucket/prefix/2021-11-01/customer-1234/file.extension`과 같은 키에 객체를 씁니다. 값을 미리 알 수 없으며 `customer_id`와 같은 속성의 경우 파티션 프로젝션 유형 `injected`를 사용하고 다음과 같이 `CREATE TABLE` 문을 사용할 수 있습니다.

```
CREATE EXTERNAL TABLE my_ingested_data3 (
 ...
)
...
PARTITIONED BY (
 day STRING,
 customer_id STRING
)
LOCATION "s3://amzn-s3-demo-bucket/prefix/"
TBLPROPERTIES (
 "projection.enabled" = "true",
 "projection.day.type" = "date",
 "projection.day.format" = "yyyy-MM-dd",
 "projection.day.range" = "2021-01-01,NOW",
 "projection.day.interval" = "1",
 "projection.day.interval.unit" = "DAYS",
 "projection.customer_id.type" = "injected",
 "storage.location.template" = "s3://amzn-s3-demo-bucket/prefix/${day}/${customer_id}/"
)
```

`injected` 유형의 파티션 키가 있는 테이블을 쿼리할 때 쿼리에 해당 파티션 키에 대한 값이 포함되어야 합니다. `my_ingested_data3` 테이블에 대한 쿼리는 다음과 같을 수 있습니다.

```
SELECT *
FROM my_ingested_data3
WHERE day BETWEEN '2021-11-01' AND '2021-11-30'
AND customer_id = 'customer-1234'
```

## 날짜 파티션 키에 DATE 형식 사용
<a name="partition-projection-kinesis-firehose-example-iso-formatted-dates"></a>

`day` 파티션 키의 값은 ISO 형식이므로 다음 예와 같이 날짜 파티션 키에 `STRING` 대신 `DATE` 유형을 사용할 수도 있습니다.

```
PARTITIONED BY (day DATE, customer_id STRING)
```

쿼리할 때 이 전략을 사용하면 다음 예와 같이 구문 분석 또는 캐스팅하지 않고 파티션 키에 날짜 함수를 사용할 수 있습니다.

```
SELECT *
FROM my_ingested_data3
WHERE day > CURRENT_DATE - INTERVAL '7' DAY
AND customer_id = 'customer-1234'
```

**참고**  
`DATE` 유형의 파티션 키를 지정하는 경우 [사용자 지정 접두사](https://docs.aws.amazon.com/firehose/latest/dev/s3-prefixes.html) 기능을 사용하여 날짜가 ISO 형식인 Amazon S3 키를 생성하는 것으로 가정합니다. `yyyy/MM/dd/HH`의 Firehose 기본 형식을 사용하는 경우 다음 예제와 같이 해당 테이블 속성이 `date` 유형이더라도 파티션 키를 `string` 유형으로 지정해야 합니다.  

```
PARTITIONED BY ( 
  `mydate` string)
TBLPROPERTIES (
  'projection.enabled'='true', 
   ...
  'projection.mydate.type'='date',
  'storage.location.template'='s3://amzn-s3-demo-bucket/prefix/${mydate}')
```

# Amazon S3 제한 방지
<a name="performance-tuning-s3-throttling"></a>

제한은 서비스, 애플리케이션 또는 시스템 사용 속도를 제한하는 프로세스입니다. AWS에서는 제한을 사용하여 Amazon S3 서비스의 과도한 사용을 방지하고 모든 사용자에 대한 Amazon S3의 가용성과 응답성을 높일 수 있습니다. 그러나 제한 기능은 Amazon S3와의 데이터 전송 속도를 제한하므로 상호 작용이 제한되지 않도록 하는 것이 중요합니다.

[성능 조정](performance-tuning.md) 장에서 설명했듯이 최적화는 서비스 수준, 테이블 및 데이터 구조, 쿼리 작성 방법에 따라 달라질 수 있습니다.

**Topics**
+ [서비스 수준에서 제한 감소](performance-tuning-s3-throttling-reduce-throttling-at-the-service-level.md)
+ [테이블 최적화](performance-tuning-s3-throttling-optimizing-your-tables.md)
+ [쿼리 최적화](performance-tuning-s3-throttling-optimizing-queries.md)

# 서비스 수준에서 제한 감소
<a name="performance-tuning-s3-throttling-reduce-throttling-at-the-service-level"></a>

서비스 수준에서 Amazon S3 제한을 방지하려면 사용량을 모니터링하고 [서비스 할당량](https://docs.aws.amazon.com/general/latest/gr/s3.html#limits_s3)을 조정하거나 파티셔닝과 같은 특정 기술을 사용할 수 있습니다. 다음은 제한으로 이어질 수 있는 몇 가지 조건입니다.
+ **계정의 API 요청 한도 초과** - Amazon S3에는 계정 유형 및 사용량에 따른 기본 API 요청 한도가 있습니다. 단일 접두사에 대한 초당 최대 요청 수를 초과하는 경우 Amazon S3 서비스의 과부하를 방지하기 위해 요청이 제한될 수 있습니다.
+ **불충분한 데이터 파티셔닝** -데이터를 적절하게 파티셔닝하지 않고 대량의 데이터를 전송하는 경우 Amazon S3에서 요청을 제한할 수 있습니다. 파티셔닝에 대한 자세한 내용은 이 문서의 [파티셔닝 사용](performance-tuning-s3-throttling-optimizing-your-tables.md#performance-tuning-s3-throttling-use-partitioning) 섹션을 참조하세요.
+ **많은 수의 작은 객체** - 가능하면 많은 수의 작은 파일을 만들지 않습니다. Amazon S3에는 파티셔닝된 접두사 1개에 대해 초당 [5,500개의 GET 요청](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html) 한도가 있으며, Athena 쿼리는 이 한도를 공유합니다. 하나의 쿼리에서 수백만 개의 작은 객체를 스캔하는 경우 쿼리는 Amazon S3에 의해 제한될 수 있습니다.

과도한 스캔을 피하려면 AWS Glue ETL을 사용하여 파일을 주기적으로 압축하거나 테이블을 분할하고 파티션 키 필터를 추가하세요. 자세한 정보는 다음 리소스를 참조하세요.
+ [더 큰 파일을 출력하도록 AWS Glue ETL 작업을 구성하려면 어떻게 해야 합니까?](https://aws.amazon.com/premiumsupport/knowledge-center/glue-job-output-large-files/) (*AWS 지식 센터*)
+ [Reading input files in larger groups](https://docs.aws.amazon.com/glue/latest/dg/grouping-input-files.html)(*AWS Glue 개발자 안내서*)

# 테이블 최적화
<a name="performance-tuning-s3-throttling-optimizing-your-tables"></a>

제한 문제가 발생하는 경우 데이터를 구조화하는 것이 중요합니다. Amazon S3는 대량의 데이터를 처리할 수 있지만 데이터가 구조화되는 방식 때문에 때때로 제한이 발생합니다.

다음 섹션에서는 제한 문제를 방지하기 위해 Amazon S3에서 데이터를 구조화하는 방법에 대한 몇 가지 제안을 제공합니다.

## 파티셔닝 사용
<a name="performance-tuning-s3-throttling-use-partitioning"></a>

파티셔닝을 사용하면 언제든지 액세스해야 하는 데이터의 양을 제한하여 제한을 줄일 수 있습니다. 특정 열에서 데이터를 파티셔닝하면 요청을 여러 객체에서 고르게 분산하고 단일 객체에 대한 요청 수를 줄일 수 있습니다. 스캔해야 하는 데이터의 양을 줄이면 쿼리 성능이 향상되고 비용이 절감됩니다.

테이블을 생성할 때 가상 열 역할을 하는 파티션을 정의할 수 있습니다. `CREATE TABLE` 문에서 파티션이 있는 테이블을 생성하려면 `PARTITIONED BY (column_name data_type)` 절을 사용하여 데이터를 파티셔닝하는 키를 정의합니다.

쿼리에서 스캔하는 파티션을 제한하기 위해 쿼리의 `WHERE` 절에서 파티션을 조건자로 지정할 수 있습니다. 따라서 필터로 자주 사용되는 열이 파티셔닝에 적합합니다. 시간 간격에 따라 데이터를 파티셔닝하는 것이 일반적이며, 이때 여러 수준의 파티셔닝 체계가 형성될 수 있습니다.

파티셔닝에도 비용이 듭니다. 테이블에서 파티션 수를 늘리면 파티션 메타데이터를 검색하고 처리하는 데 필요한 시간도 늘어납니다. 따라서 과도하게 파티셔닝하면 보다 신중한 파티셔닝으로 얻을 수 있는 혜택이 사라질 수 있습니다. 데이터가 한 파티션 값으로 심하게 편중되고 대부분의 쿼리에서 이 값을 사용하는 경우 추가 오버헤드가 발생할 수 있습니다.

Athena에서 파티셔닝에 대한 자세한 내용은 [파티셔닝이란 무엇인가요?](ctas-partitioning-and-bucketing-what-is-partitioning.md) 섹션을 참조하세요.

## 데이터 버킷팅
<a name="performance-tuning-s3-throttling-bucket-your-data"></a>

데이터를 파티셔닝하는 또 다른 방법은 데이터를 단일 파티션 내에 버킷팅하는 것입니다. 버킷팅에서는 함께 그룹화하려는 행이 포함된 하나 이상의 열을 지정합니다. 그런 다음 해당 행을 여러 버킷에 넣습니다. 이 방식으로 읽어야 하는 버킷만 쿼리하여 스캔해야 하는 데이터 행 수를 줄일 수 있습니다.

버킷팅에 사용할 열을 선택하는 경우 카디널리티가 높고(즉, 개별 값이 많음) 균일하게 분산되었으며 데이터를 필터링하는 데 자주 사용되는 열을 선택합니다. 버킷팅에 사용하기에 좋은 열의 예로는 ID 열과 같은 기본 키가 있습니다.

Athena에서 버킷팅 사용에 대한 자세한 내용은 [버킷팅이란 무엇인가요?](ctas-partitioning-and-bucketing-what-is-bucketing.md) 섹션을 참조하세요.

## AWS Glue 파티션 인덱스 사용
<a name="performance-tuning-s3-throttling-use-aws-glue-partition-indexes"></a>

AWS Glue 파티션 인덱스를 사용하여 하나 이상의 파티션 값을 기반으로 테이블의 데이터를 구성할 수 있습니다. AWS Glue 파티션 인덱스를 사용하면 데이터 전송 횟수, 데이터 처리량, 쿼리 처리 시간을 줄일 수 있습니다.

AWS Glue 파티션 인덱스는 파티션 키 및 해당 값을 포함하여 테이블의 파티션에 대한 정보가 들어 있는 메타데이터 파일입니다. 파티션 인덱스는 Amazon S3 버킷에 저장되며 테이블에 새 파티션이 추가되면 AWS Glue에 의해 자동으로 업데이트됩니다.

AWS Glue 파티션 인덱스가 존재하면 테이블의 모든 파티션을 로드하는 대신 쿼리에서 파티션의 하위 세트를 가져오려고 합니다. 쿼리는 쿼리와 관련된 데이터의 하위 세트에서만 실행됩니다.

AWS Glue에서 테이블을 생성할 때 테이블에 정의된 파티션 키 조합에서 파티션 인덱스를 생성할 수 있습니다. 테이블에서 파티션 인덱스를 하나 이상 생성한 후에는 파티션 필터링을 활성화하는 속성을 테이블에 추가해야 합니다. 그런 다음 Athena에서 테이블을 쿼리할 수 있습니다.

AWS Glue에서 파티션 인덱스 생성에 대한 자세한 내용은 *AWS Glue 개발자 안내서*의 [Working with partition indexes in AWS Glue](https://docs.aws.amazon.com/glue/latest/dg/partition-indexes.html)를 참조하세요. 파티션 필터링을 활성화하기 위해 테이블 속성을 추가하는 방법에 대한 자세한 내용은 [AWS Glue 파티션 인덱싱 및 필터링을 통한 쿼리 최적화](glue-best-practices-partition-index.md) 섹션을 참조하세요.

## 데이터 압축 및 파일 분할 사용
<a name="performance-tuning-s3-throttling-use-data-compression-and-file-splitting"></a>

파일이 최적 크기이거나 파일을 논리적 그룹으로 분할할 수 있는 경우 데이터 압축을 통해 쿼리 속도를 크게 높일 수 있습니다. 일반적으로 압축률이 높을수록 데이터를 압축하고 압축 해제하는 데 더 많은 CPU 사이클이 필요합니다. Athena의 경우 기본적으로 데이터를 압축하는 Apache Parquet 또는 Apache ORC를 사용하는 것이 좋습니다. Athena의 데이터 압축에 대한 자세한 내용은 [Athena에서 압축 사용](compression-formats.md) 섹션을 참조하세요.

파일을 분할하면 Athena에서 단일 파일을 읽는 작업을 여러 리더에 분산시켜 병렬 처리 성능을 높일 수 있습니다. 단일 파일을 분할할 수 없는 경우 하나의 리더만 파일을 읽고 다른 리더가 유휴 상태로 둘 수 있습니다. Apache Parquet 및 Apache ORC도 분할 가능한 파일을 지원합니다.

## 최적화된 열 기반 데이터 스토어 사용
<a name="performance-tuning-s3-throttling-use-optimized-columnar-data-stores"></a>

데이터를 열 형식으로 변환하면 Athena 쿼리 성능이 크게 향상됩니다. 열 기반 파일을 생성할 때 고려할 한 가지 최적화 기법은 파티션 키를 기준으로 데이터를 정렬하는 것입니다.

Apache Parquet 및 Apache ORC는 흔히 사용되는 오픈 소스 열 기반 데이터 스토어입니다. 기존 Amazon S3 데이터 소스를 이러한 형식 중 하나로 변환하는 방법에 대한 자세한 내용은 [열 기반 형식으로 변환](columnar-storage.md#convert-to-columnar) 섹션을 참조하세요.

### 하나의 큰 Parquet 블록 크기 또는 ORC 스트라이프 크기 사용
<a name="performance-tuning-s3-throttling-use-a-larger-parquet-block-size-or-orc-stripe-size"></a>

Parquet 및 ORC에는 최적화를 위해 조정할 수 있는 데이터 스토리지 파라미터가 있습니다. Parquet에서는 블록 크기를 최적화할 수 있습니다. ORC에서는 스트라이프 크기를 최적화할 수 있습니다. 블록이나 스트라이프가 클수록 각 블록이나 스트라이프에 저장할 수 있는 행 수가 많아집니다. 기본적으로 Parquet 블록 크기는 128MB이고 ORC 스트라이프 크기는 64MB입니다.

ORC 스트라이프가 8MB(기본값 `hive.orc.max_buffer_size`) 미만인 경우 Athena는 전체 ORC 스트라이프를 읽습니다. 스트라이프 크기가 작은 경우 Athena는 초당 입력 및 출력 작업과 열 선택성 사이에서 이와 같이 절충합니다.

열 수가 매우 많은 테이블의 경우 블록 또는 스트라이프 크기가 작으면 필요 이상으로 많은 데이터가 스캔될 수 있습니다. 이 경우에는 블록 크기가 클수록 효율적입니다.

### 복잡한 유형에 대해 ORC 사용
<a name="performance-tuning-s3-throttling-use-orc-for-complex-types"></a>

현재 Parquet에 저장된 복잡한 데이터 형식(예: `array`, `map` 또는 `struct`)의 열을 쿼리하는 경우 Athena는 지정된 열만 선택적으로 읽는 대신, 전체 데이터 행을 읽습니다. 이것은 Athena에서 알려진 문제입니다. 이 문제를 해결하려면 ORC를 사용합니다.

### 압축 알고리즘 선택
<a name="performance-tuning-s3-throttling-choose-a-compression-algorithm"></a>

사용자가 구성할 수 있는 또 다른 파라미터는 데이터 블록의 압축 알고리즘입니다. Athena에서 Parquet 및 ORC용으로 지원되는 압축 알고리즘에 대한 자세한 내용은 [Athena 압축 지원](https://docs.aws.amazon.com/athena/latest/ug/compression-formats.html)을 참조하세요.

Athena에서 열 기반 스토리지 형식 최적화에 대한 자세한 내용은 AWS 빅 데이터 블로그 게시물 [Top 10 Performance Tuning Tips for Amazon Athena](https://aws.amazon.com/blogs/big-data/top-10-performance-tuning-tips-for-amazon-athena/)의 'Optimize columnar data store generation' 섹션을 참조하세요.

## Iceberg 테이블 사용
<a name="performance-tuning-s3-throttling-use-iceberg-tables"></a>

Apache Iceberg는 Amazon S3에서 최적화된 사용을 위해 설계된 초대형 분석 데이터 세트를 위한 오픈 테이블 형식입니다. Iceberg 테이블을 사용하면 Amazon S3에서 제한을 줄이는 데 도움이 될 수 있습니다.

Iceberg 테이블은 다음과 같은 이점을 제공합니다.
+ Iceberg 테이블은 하나 이상의 열로 분할할 수 있습니다. 이렇게 하면 데이터 액세스가 최적화되고 쿼리로 스캔해야 하는 데이터의 양이 줄어듭니다.
+ Iceberg 객체 스토리지 모드는 Amazon S3에서 사용할 수 있도록 Iceberg 테이블을 최적화하므로 대량의 데이터와 많은 쿼리 워크로드를 처리할 수 있습니다.
+ 객체 스토리지 모드의 Iceberg 테이블은 확장 가능하고 내결함성이 뛰어나며 내구성이 뛰어나므로 제한을 줄이는 데 도움이 될 수 있습니다.
+ ACID 트랜잭션 지원이란, 여러 사용자가 원자 단위로 Amazon S3 객체를 추가하고 삭제할 수 있음을 의미합니다.

Apache Iceberg에 대한 자세한 내용은 [Apache Iceberg](https://iceberg.apache.org/)를 참조하세요. Athena에서 Apache Iceberg 테이블 사용에 대한 자세한 내용은 [Iceberg 테이블 사용](https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg.html)을 참조하세요.

# 쿼리 최적화
<a name="performance-tuning-s3-throttling-optimizing-queries"></a>

Athena에서 SQL 쿼리를 최적화하려는 경우 이 섹션의 제안 사항을 사용합니다.

## ORDER BY 절과 함께 LIMIT 사용
<a name="performance-tuning-s3-throttling-use-limit-with-the-order-by-clause"></a>

`ORDER BY` 절은 정렬된 순서로 데이터를 반환합니다. 이를 위해 Athena에서 모든 데이터 행을 단일 워커 노드로 보낸 후 행을 정렬해야 합니다. 이러한 유형의 쿼리는 오래 실행되거나 실패할 수도 있습니다.

쿼리의 효율성을 높이려면 상위 또는 하위 *N*개 값을 확인하고 `LIMIT` 절도 사용합니다. 그러면 정렬과 제한을 단일 작업자가 아닌 개별 워커 노드로 푸시하여 정렬 비용을 크게 줄일 수 있습니다.

## JOIN 절 최적화
<a name="performance-tuning-s3-throttling-optimize-join-clauses"></a>

두 테이블을 조인하는 경우 Athena는 오른쪽에 있는 테이블을 워커 노드에 분산시킨 후 왼쪽의 테이블을 스트리밍하여 조인을 수행합니다.

따라서 조인 왼쪽에 큰 테이블을 지정하고 오른쪽에 작은 테이블을 지정합니다. 이렇게 하면 Athena는 메모리를 덜 사용하고 지연 시간을 줄이면서 쿼리를 실행합니다.

다음 사항에도 주의하세요.
+ 여러 `JOIN` 명령을 사용하는 경우 가장 큰 테이블부터 가장 작은 테이블의 순서로 지정합니다.
+ 쿼리에 필요하지 않는 한 크로스 조인을 사용하지 않습니다.

## GROUP BY 절 최적화
<a name="performance-tuning-s3-throttling-optimize-group-by-clauses"></a>

`GROUP BY` 연산자는 `GROUP BY` 열을 기반으로 워커 노드에 행을 분산시킵니다. 이러한 열은 메모리에서 참조되며 행을 모을 때 값이 비교됩니다. `GROUP BY` 열이 일치하면 값이 함께 집계됩니다. 이 프로세스의 작동 방식을 고려하면 카디널리티가 가장 높은 열부터 가장 낮은 열의 순서로 정렬하는 것이 좋습니다.

## 문자열 대신 숫자 사용
<a name="performance-tuning-s3-throttling-use-numbers-instead-of-strings"></a>

숫자는 문자열에 비해 메모리 사용량이 적고 처리 속도가 빠르므로 가능하면 문자열 대신 숫자를 사용합니다.

## 열 수 제한
<a name="performance-tuning-s3-throttling-limit-the-number-of-columns"></a>

데이터를 저장하는 데 필요한 총 메모리 양을 줄이려면 `SELECT` 문에 지정된 열 수를 제한합니다.

## LIKE 대신 정규식 사용
<a name="performance-tuning-s3-throttling-use-regular-expressions-instead-of-like"></a>

큰 문자열에서 `LIKE '%string%'`과 같은 절을 포함하는 쿼리는 컴퓨팅 집약적일 수 있습니다. 문자열 열에서 여러 값을 필터링하는 경우 대신 [regexp\$1like()](https://trino.io/docs/current/functions/regexp.html#regexp_like) 함수와 정규식을 사용합니다. 값의 긴 목록을 비교할 때 특히 유용합니다.

## LIMIT 절 사용
<a name="performance-tuning-s3-throttling-use-the-limit-clause"></a>

쿼리를 실행할 때 모든 열을 선택하는 대신 `LIMIT` 절을 사용하여 필요한 열만 반환합니다. 이렇게 하면 쿼리 실행 파이프라인을 통해 처리되는 데이터 세트의 크기가 줄어듭니다. `LIMIT` 절은 문자열 기반 열 수가 많은 테이블을 쿼리할 때 더 유용합니다. `LIMIT` 절은 쿼리에서 여러 조인 또는 집계를 수행할 때 유용합니다.

# 추가 리소스
<a name="performance-tuning-additional-resources"></a>

Athena 성능 튜닝에 대한 추가 정보는 다음 리소스를 고려하세요.
+ AWS 빅 데이터 블로그 게시물: [Top 10 performance tuning tips for Amazon Athena](https://aws.amazon.com/blogs/big-data/top-10-performance-tuning-tips-for-amazon-athena/)(Amazon Athena의 성능 튜닝을 위한 10가지 팁).
+ AWS 빅 데이터 블로그 게시물: *AWS 빅 데이터 블로그*의 [Run queries 3x faster with up to 70% cost savings on the latest Amazon Athena engine](https://aws.amazon.com/blogs/big-data/run-queries-3x-faster-with-up-to-70-cost-savings-on-the-latest-amazon-athena-engine/)(최신 Amazon Athena 엔진을 사용하면 3배 더 빠르게 쿼리를 실행하면서 최대 70%까지 비용 절감).
+ AWS 빅 데이터 블로그 게시물: [Improve federated queries with predicate pushdown in Amazon Athena](https://aws.amazon.com/blogs/big-data/improve-federated-queries-with-predicate-pushdown-in-amazon-athena/)(Amazon Athena에서 조건자 푸시다운을 사용하여 페더레이션 쿼리 개선).
+ Amazon Simple Storage Service 사용 설명서: [모범 사례 설계 패턴: Amazon S3 성능 최적화](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html).
+ [AWS 빅 데이터 블로그의 다른 Athena 게시물](https://aws.amazon.com/blogs/big-data/tag/amazon-athena/) 
+ **Amazon Athena** 태그를 사용하여 [AWS re:Post](https://repost.aws/tags/TA78iVOM7gR62_QqDe2-CmiA/amazon-athena)에 대해 질문하기.
+ [AWS 지식 센터의 Athena 주제](https://aws.amazon.com/premiumsupport/knowledge-center/#Amazon_Athena) 참조.
+ AWS Support에 문의(AWS Management Console에서 **지원**, **지원 센터** 클릭)