Apache Kudu 파티셔닝 완전 가이드: Hash, Range, 그리고 Multilevel 전략
Kudu 테이블의 Hash Partitioning, Range Partitioning, Multilevel Partitioning 전략을 실제 예제와 함께 비교하고, Partition Pruning 동작 원리와 설계 시 주의사항을 정리합니다.
Apache Kudu 에서 테이블을 생성할 때 파티셔닝은 선택이 아닌 필수 입니다. Kudu 는 기본 파티셔닝 전략을 제공하지 않으므로, 테이블을 만들 때 반드시 파티셔닝 방식을 명시해야 합니다. 잘못된 파티셔닝은 쓰기 핫스팟, 읽기 성능 저하, 무한정 커지는 tablet 등 다양한 문제를 유발합니다. 이 글은 Kudu 공식 Schema Design 문서의 Partitioning 섹션 을 바탕으로, 각 파티셔닝 전략의 특성과 실무 적용법을 정리합니다.
1. 파티셔닝의 기본 개념
Kudu 테이블은 tablet 이라는 단위로 분할되어 여러 Tablet Server 에 분산 저장됩니다. 하나의 row 는 항상 하나의 tablet 에만 속하며, 어떤 row 가 어떤 tablet 에 배치되는지는 테이블 생성 시 정의한 파티셔닝 전략에 의해 결정됩니다.
파티셔닝 전략은 테이블 생성 이후에는 변경할 수 없습니다 (Range Partition 의 추가/삭제만 예외). 따라서 설계 시점에 신중한 결정이 필요합니다.
Kudu 가 제공하는 파티셔닝 방식은 크게 세 가지입니다:
| 파티셔닝 방식 | 설명 |
|---|---|
| Hash Partitioning | Primary Key 컬럼의 해시값으로 bucket 에 분배 |
| Range Partitioning | Primary Key 컬럼의 값 범위로 연속적인 구간에 분배 |
| Multilevel Partitioning | Hash + Range 또는 Hash + Hash 를 조합 |
이 글에서는 Kudu 공식 문서에서 사용하는 metrics 테이블 예제를 기반으로 각 전략을 설명합니다.
CREATE TABLE metrics (
host STRING NOT NULL,
metric STRING NOT NULL,
time INT64 NOT NULL,
value DOUBLE NOT NULL,
PRIMARY KEY (host, metric, time)
);이 테이블은 여러 호스트에서 수집되는 시계열 메트릭 데이터를 저장합니다. host, metric, time 의 조합이 Primary Key 이며, 파티셔닝 전략에 따라 성능이 크게 달라집니다.
2. Range Partitioning
2.1 동작 원리
Range Partitioning 은 정렬된 범위(range) 를 기준으로 row 를 분배합니다. 각 파티션은 키 공간의 연속적인 구간을 담당합니다. Range Partition 의 컬럼은 반드시 Primary Key 의 부분 집합 이어야 합니다.
CREATE TABLE metrics (
host STRING NOT NULL,
metric STRING NOT NULL,
time INT64 NOT NULL,
value DOUBLE NOT NULL,
PRIMARY KEY (host, metric, time)
)
PARTITION BY RANGE (time) (
PARTITION VALUE = 1,
PARTITION VALUE = 2,
PARTITION VALUE = 3
)
STORED AS KUDU;2.2 장점
- 시간 기반 스캔 최적화: 시간 범위 조건이 있는 쿼리는 해당 범위에 속하지 않는 파티션을 자동으로 건너뜀 (Partition Pruning)
- 동적 파티션 관리: 런타임에 파티션을 추가하거나 삭제할 수 있으며, 다른 파티션의 가용성에 영향을 주지 않음
- 효율적인 데이터 삭제: 파티션을 삭제하면 해당 파티션의 모든 데이터가 함께 삭제됨. row 단위 DELETE 후 compaction 을 기다릴 필요 없이 즉시 디스크 공간 회수 가능
2.3 단점
- 쓰기 핫스팟: 시계열 데이터의 경우 현재 시점의 데이터가 항상 하나의 파티션에 집중됨. 이로 인해 특정 Tablet Server 에 쓰기 부하가 몰림
- 병렬성 제한: 쓰기가 하나의 tablet 에 집중되면 다른 Tablet Server 의 자원이 유휴 상태로 낭비됨
핵심: Range Partitioning 단독 사용은 읽기에는 유리하지만 쓰기에는 불리 합니다. 시계열 데이터에서 Range 만 사용하면 최신 데이터 파티션이 핫스팟이 됩니다.
3. Hash Partitioning
3.1 동작 원리
Hash Partitioning 은 지정된 컬럼의 해시값 을 기반으로 row 를 여러 bucket 에 분배합니다. bucket 수는 테이블 생성 시 결정되며 이후 변경할 수 없습니다. Primary Key 컬럼의 부분 집합을 해시 대상으로 사용할 수 있습니다.
CREATE TABLE metrics (
host STRING NOT NULL,
metric STRING NOT NULL,
time INT64 NOT NULL,
value DOUBLE NOT NULL,
PRIMARY KEY (host, metric, time)
)
PARTITION BY HASH (host, metric) PARTITIONS 8
STORED AS KUDU;3.2 장점
- 쓰기 분산: 해시 함수가 row 를 고르게 분배하므로 쓰기 핫스팟이 발생하지 않음
- 균등한 tablet 크기: 데이터가 bucket 간에 고르게 분포
- 특정 host/metric 조회 최적화:
host와metric에 대한 동등 조건(equality predicate) 이 있으면 Partition Pruning 가능
3.3 단점
- tablet 무한 성장: Range Partition 처럼 파티션을 추가/삭제할 수 없으므로, 데이터가 계속 쌓이면 tablet 이 무한정 커짐
- 시간 범위 조회 비효율: time 컬럼이 해시에 포함되지 않았으므로 시간 범위 조건만으로는 Partition Pruning 이 불가능
- 데이터 삭제 비용: 오래된 데이터를 삭제하려면 row 단위 DELETE 를 수행해야 하며, 실제 디스크 공간은 compaction 이 완료될 때까지 회수되지 않음
핵심: Hash Partitioning 단독 사용은 쓰기에는 유리하지만 tablet 의 무한 성장이 문제 입니다. 장기간 운영 시 tablet 크기가 감당할 수 없을 만큼 커질 수 있습니다.
4. Multilevel Partitioning (Hash + Range 조합)
4.1 동작 원리
Kudu 는 여러 단계의 파티셔닝을 하나의 테이블에 조합 할 수 있습니다. 0개 이상의 Hash Partition 레벨과 선택적인 Range Partition 레벨을 함께 사용할 수 있습니다. 전체 tablet 수는 각 레벨의 파티션 수를 곱한 값 입니다.
CREATE TABLE metrics (
host STRING NOT NULL,
metric STRING NOT NULL,
time INT64 NOT NULL,
value DOUBLE NOT NULL,
PRIMARY KEY (host, metric, time)
)
PARTITION BY
HASH (host, metric) PARTITIONS 8,
RANGE (time) (
PARTITION VALUE = 1,
PARTITION VALUE = 2,
PARTITION VALUE = 3
)
STORED AS KUDU;이 예제에서 전체 tablet 수는 8 (Hash buckets) x 3 (Range partitions) = 24개 입니다.
4.2 Hash + Range 의 장점
이 조합은 Hash 와 Range 각각의 장점을 결합하고 단점을 보완합니다.
| 관점 | Hash 단독 | Range 단독 | Hash + Range |
|---|---|---|---|
| 쓰기 분산 | 균등 분산 | 핫스팟 발생 | 균등 분산 |
| 시간 범위 읽기 | Pruning 불가 | Pruning 가능 | Pruning 가능 |
| 특정 host/metric 읽기 | Pruning 가능 | Pruning 불가 | Pruning 가능 |
| tablet 성장 관리 | 무한 성장 | 동적 추가/삭제 | 동적 추가/삭제 |
| 오래된 데이터 삭제 | row 단위 DELETE 필요 | 파티션 DROP 으로 즉시 삭제 | 파티션 DROP 으로 즉시 삭제 |
- 쓰기: Hash 가
host와metric을 기준으로 여러 bucket 에 분산하므로, 같은 시간대의 쓰기도 여러 tablet 에 병렬로 처리됨 - 읽기: 시간 범위 조건으로 Range Partition Pruning, host/metric 동등 조건으로 Hash Partition Pruning 이 동시에 동작
- 관리: 오래된 Range Partition 을 DROP 하면 해당 시간 범위의 데이터가 즉시 삭제되고 디스크 공간이 회수됨
4.3 Hash + Hash 조합
Hash 를 여러 단계로 조합하는 것도 가능합니다. 단, 서로 다른 Hash 레벨이 같은 컬럼을 해시해서는 안 됩니다.
CREATE TABLE metrics (
host STRING NOT NULL,
metric STRING NOT NULL,
time INT64 NOT NULL,
value DOUBLE NOT NULL,
PRIMARY KEY (host, metric, time)
)
PARTITION BY
HASH (host) PARTITIONS 4,
HASH (metric) PARTITIONS 4
STORED AS KUDU;이 경우 전체 tablet 수는 4 x 4 = 16개 입니다.
Hash + Hash 조합의 특징:
- 쓰기가 모든 tablet 에 골고루 분산됨
- 그러나 Range Partition 이 없으므로 tablet 이 무한정 성장 하고, 오래된 데이터를 파티션 단위로 삭제할 수 없음
- 단일 Hash 레벨에서 모든 컬럼을 해시하는 것보다 핫스팟에 약간 더 취약할 수 있음
권장: 시계열 데이터에는 Hash + Range 조합 을 사용하세요. Hash + Hash 는 시간 기반 데이터 관리가 필요 없는 경우에만 적합합니다.
5. Flexible Partitioning (Range 별 Hash 스키마)
Kudu 1.17 이상부터는 Range 파티션별로 서로 다른 Hash 스키마 를 적용할 수 있습니다. 이를 통해 특정 Range 구간에서 발생하는 핫스팟을 더 세밀하게 제어할 수 있습니다.
예를 들어, 최근 데이터는 Hash bucket 수를 많이 잡아 쓰기를 분산하고, 오래된 데이터는 Hash bucket 수를 줄여 tablet 수를 절약하는 전략이 가능합니다.
단, 주의할 점이 있습니다: 모든 Range 에서 Hash Partition 레벨의 수(단계 수)는 동일해야 합니다. bucket 수는 다를 수 있지만, Hash 레벨 자체의 개수는 맞춰야 합니다.
6. Partition Pruning (파티션 가지치기)
Partition Pruning 은 Kudu 의 핵심 성능 최적화 메커니즘입니다. 스캔 시 조건(predicate)에 의해 특정 파티션이 완전히 필터링될 수 있으면, 해당 파티션의 tablet 을 아예 스캔하지 않습니다.
6.1 Pruning 조건
| 파티셔닝 방식 | Pruning 이 동작하는 조건 |
|---|---|
| Hash Partition | 해시에 사용된 모든 컬럼 에 대한 동등 조건(equality predicate) 이 있어야 함 |
| Range Partition | Range 컬럼에 대한 동등 조건 또는 범위 조건(range predicate) 이 있어야 함 |
| Multilevel | 각 레벨에 대해 독립적으로 Pruning 가능 |
6.2 Pruning 예제 (Hash + Range)
HASH (host, metric) PARTITIONS 8, RANGE (time) 파티셔닝이 적용된 metrics 테이블에서:
| 쿼리 조건 | Hash Pruning | Range Pruning | 효과 |
|---|---|---|---|
WHERE host = 'a' AND metric = 'cpu' AND time >= 100 AND time < 200 | 가능 (8→1 bucket) | 가능 (해당 범위만) | 최적 — 소수의 tablet 만 스캔 |
WHERE host = 'a' AND metric = 'cpu' | 가능 (8→1 bucket) | 불가 | Hash Pruning 만 동작 |
WHERE time >= 100 AND time < 200 | 불가 | 가능 (해당 범위만) | Range Pruning 만 동작 |
WHERE host = 'a' | 불가 (metric 조건 없음) | 불가 | Pruning 없음 — full scan |
중요: Hash Partition Pruning 은 해시에 포함된 모든 컬럼에 대한 동등 조건이 있어야만 동작합니다. 하나라도 빠지면 Pruning 이 불가능합니다.
7. 파티셔닝 전략 비교 요약
공식 문서에서 제시하는 metrics 테이블 기준 비교를 확장하면:
| 전략 | 쓰기 성능 | 읽기 성능 | tablet 성장 관리 | 데이터 삭제 | Pruning |
|---|---|---|---|---|---|
RANGE (time) | 나쁨 (핫스팟) | 좋음 (시간 Pruning) | 좋음 (동적 관리) | 좋음 (DROP) | Range 만 |
HASH (host, metric) | 좋음 (분산) | 좋음 (host/metric Pruning) | 나쁨 (무한 성장) | 나쁨 (row DELETE) | Hash 만 |
HASH (host, metric) + RANGE (time) | 좋음 (분산) | 좋음 (양쪽 Pruning) | 좋음 (동적 관리) | 좋음 (DROP) | 양쪽 모두 |
HASH (host) + HASH (metric) | 좋음 (분산) | 제한적 | 나쁨 (무한 성장) | 나쁨 (row DELETE) | Hash 만 |
8. 실무 설계 가이드라인
8.1 tablet 수 결정
테이블 생성 시 tablet 수는 다음 공식으로 결정됩니다:
총 tablet 수 = Hash bucket 수 × Range partition 수
읽기/쓰기 부하가 높은 테이블은 최소한 Tablet Server 수 이상의 tablet 을 갖도록 설계하여 부하가 모든 서버에 분산되도록 합니다.
| 클러스터 규모 | 최소 권장 tablet 수 | Hash bucket 수 예시 |
|---|---|---|
| Tablet Server 3대 | 3개 이상 | 4 또는 8 |
| Tablet Server 5대 | 5개 이상 | 8 |
| Tablet Server 10대 | 10개 이상 | 8 또는 16 |
8.2 Hash Partition 설계 시 고려사항
- Hash 컬럼 선택: 카디널리티가 높은 컬럼을 선택해야 데이터가 bucket 간에 고르게 분포됨
- bucket 수: 일반적으로 Tablet Server 수의 배수로 설정. 너무 많으면 tablet 수 폭증, 너무 적으면 병렬성 부족
- 변경 불가: Hash bucket 수는 테이블 생성 후 변경할 수 없으므로 신중하게 결정
8.3 Range Partition 설계 시 고려사항
- 파티션 단위: 일별보다 월별 파티션 을 우선 검토. 일별 파티션은 tablet 수를 30배 증가시킴
- 미래 파티션: 미리 대량으로 생성하지 말고, 필요한 시점에 동적으로 추가
- 데이터 보관 정책: 오래된 파티션을 DROP 하면 즉시 디스크 공간 회수 가능. row 단위 DELETE 보다 훨씬 효율적
- 비어 있는 파티션: 데이터가 없는 Range Partition 도 tablet 을 유지하므로 WAL 디스크를 소비함
8.4 디스크 공간 회수에 대한 이해
Kudu 에서 row 단위로 데이터를 DELETE 하면, 삭제된 row 가 차지하던 디스크 공간은 compaction 이 완료될 때까지 즉시 회수되지 않습니다. 대량의 과거 데이터를 삭제해야 하는 경우, Range Partition 을 사용하여 파티션 단위로 DROP 하는 것이 디스크 공간을 즉시 회수하는 유일한 방법입니다.
-- 비효율적: row 단위 삭제 → 디스크 공간은 compaction 후에야 회수
DELETE FROM metrics WHERE time < 1000;
-- 효율적: 파티션 단위 삭제 → 즉시 디스크 공간 회수
ALTER TABLE metrics DROP RANGE PARTITION VALUE = 1;9. 파티셔닝 설계 체크리스트
| 항목 | 확인 |
|---|---|
| 파티셔닝 전략을 명시적으로 정의했는가? (Kudu 는 기본 파티셔닝을 제공하지 않음) | |
| 시계열 데이터라면 Hash + Range 조합을 사용했는가? | |
| Hash 컬럼으로 카디널리티가 높은 컬럼을 선택했는가? | |
| Hash bucket 수가 Tablet Server 수의 배수에 가까운가? | |
| Range Partition 단위로 일별 대신 월별을 검토했는가? | |
| 전체 tablet 수 (Hash buckets × Range partitions) 가 Tablet Server 당 1,000개 이하인가? | |
| 오래된 데이터 삭제 전략이 Range Partition DROP 기반인가? | |
| 미래 Range Partition 을 과도하게 미리 생성하지 않았는가? | |
| Partition Pruning 이 주요 쿼리 패턴에서 동작하는지 확인했는가? | |
| Hash Pruning 을 위한 동등 조건이 쿼리에 포함되는가? |
10. 정리
| 항목 | 내용 |
|---|---|
| 기본 파티셔닝 | 제공되지 않음 — 반드시 명시적으로 설계 |
| 권장 조합 (시계열) | Hash + Range |
| Hash Partition 변경 | 불가능 (bucket 수, 컬럼 모두) |
| Range Partition 변경 | 추가/삭제만 가능 |
| Hash Pruning 조건 | 해시 컬럼 전체 에 대한 동등 조건 필수 |
| Range Pruning 조건 | Range 컬럼에 대한 동등 또는 범위 조건 |
| tablet 수 공식 | Hash buckets × Range partitions |
| 디스크 공간 즉시 회수 | Range Partition DROP 만 가능 (row DELETE 는 compaction 후) |
| Flexible Partitioning | Kudu 1.17+ 에서 Range 별 Hash bucket 수 다르게 설정 가능 |
파티셔닝은 Kudu 테이블 설계에서 가장 중요한 결정 입니다. 한 번 정하면 바꿀 수 없고 (Range 추가/삭제 제외), 쓰기/읽기 성능, 데이터 관리, 디스크 사용량 모두에 직접적인 영향을 미칩니다. 테이블을 만들기 전에 쿼리 패턴, 데이터 증가율, 보관 정책을 충분히 분석하고, Hash + Range 조합을 기본 전략으로 검토하세요.
이 글은 Apache Kudu 공식 Schema Design Guide — Partitioning 을 참고하여 작성되었습니다. Kudu 파티셔닝 설계나 운영에 대해 도움이 필요하시면 언제든 문의해 주세요.
— Data Dynamics 엔지니어링 팀