Apache Kudu 테이블 설계 시 반드시 알아야 할 기능적 제약
파티션 개수, 컬럼 수 제한, Primary Key 설계 순서 등 Kudu 를 실무에서 운영할 때 놓치기 쉬운 제약 사항들을 정리합니다.
Apache Kudu 는 빠른 분석 쿼리와 실시간 업서트를 동시에 지원하는 강력한 스토리지 엔진이지만, 테이블을 설계할 때 반드시 인지해야 할 기능적 제약 들이 존재합니다. 이 제약들을 무시하고 테이블을 만들면 디스크 공간 낭비, 성능 저하, 심지어 클러스터 불안정으로 이어질 수 있습니다. 이 글은 Kudu 공식 Known Issues and Limitations 문서 와 Schema Design 가이드 를 바탕으로, 실무에서 놓치기 쉬운 제약 사항들을 정리합니다.
1. 파티션(Tablet) 개수 제한
Kudu 에서 테이블은 여러 개의 tablet 으로 분할되어 각 Tablet Server 에 분산 저장됩니다. 파티셔닝은 병렬 처리의 핵심이지만, 파티션 수를 무분별하게 늘리면 심각한 부작용이 발생합니다.
1.1 파티션당 고정 오버헤드
각 tablet 은 데이터가 없더라도 수 MB 의 디스크 공간 을 차지합니다. 이는 tablet 마다 유지되는 메타데이터, WAL(Write-Ahead Log), consensus 상태 정보 등 때문입니다.
| 항목 | 대략적 크기 |
|---|---|
| Tablet 메타데이터 | 수백 KB ~ 수 MB |
| WAL 세그먼트 (최소 1개) | 수 MB |
| Consensus 메타데이터 | 수십 KB |
| Bloom filter, Primary Key 인덱스 | 데이터 양에 비례하지만 최소 크기 존재 |
예를 들어, hash partition 16개 x range partition 365개(1년 일별) = 5,840개 tablet 을 만들면, 데이터가 한 건도 없어도 수십 GB 의 디스크 공간 이 소모됩니다. 이것은 실제 데이터가 아니라 순수하게 파티션 메타데이터와 구조 유지 비용입니다.
1.2 권장 기준
Kudu 공식 문서에서 제시하는 가이드라인은 다음과 같습니다.
| 기준 | 권장값 |
|---|---|
| Tablet Server 당 tablet 수 | 1,000개 이하 (이상적으로 수백 개) |
| 테이블당 tablet 수 | 클러스터 전체 Tablet Server 수 × 수십 개 이하 |
| tablet 당 권장 데이터 크기 | 1 GB ~ 10 GB |
핵심 원칙: tablet 수를 늘려서 병렬성을 높이는 것보다, 적절한 크기의 tablet 을 유지하는 것 이 성능과 안정성 모두에 유리합니다.
1.3 파티션이 너무 많으면 생기는 문제
- WAL 디스크 용량 폭증: 이 문제는 특히 심각합니다. 각 tablet 은 독립적인 WAL(Write-Ahead Log) 세그먼트를 유지하며, 데이터가 없는 빈 tablet 이라도 WAL 파일은 반드시 존재합니다. 파티션 수가 급격히 늘어나면 WAL 디스크 사용량이 실제 데이터 크기와 무관하게 기하급수적으로 증가 합니다. 초기 하드웨어 용량 산정 시 WAL 디스크를 데이터 디스크 대비 작게 잡는 경우가 많은데, 파티션 수가 예상보다 많아지면 WAL 디스크가 먼저 가득 차면서 새로운 테이블을 생성할 수 없거나 기존 테이블에 range partition 을 추가할 수 없는 상황 이 발생합니다. WAL 디스크 부족은 Tablet Server crash 로 이어질 수 있으므로, 하드웨어 용량 산정 시 클러스터 전체의 총 tablet 수를 기준으로 WAL 디스크 크기를 넉넉하게 계산 해야 합니다.
- 디스크 공간 낭비: 빈 tablet 도 수 MB 씩 차지하므로, 파티션 수 자체가 디스크 소비의 주요 원인이 됨
- 메모리 압박: 각 tablet 은 메모리 내에 MemRowSet, DeltaMemStore 등을 유지. tablet 수가 많아지면 Tablet Server 의 메모리가 부족해짐
- Master 부하 증가: Kudu Master 는 모든 tablet 의 위치 정보를 관리. tablet 수가 지나치게 많으면 Master 의 heartbeat 처리와 tablet location lookup 이 느려짐
- Compaction 경합: tablet 마다 독립적으로 compaction 이 수행되므로, tablet 이 많으면 I/O 경합이 심해짐
- 리더 선출 폭풍: Tablet Server 재시작 시 수천 개의 tablet 이 동시에 리더 선출을 시작하면 클러스터 전체가 수 분간 불안정해짐
- 클러스터 전체 성능 저하: 파티션 수가 전체 클러스터의 처리 능력을 초과하면 모든 테이블의 읽기/쓰기 성능이 동반 하락합니다. 이는 특정 테이블만의 문제가 아니라 클러스터에 존재하는 모든 테이블에 영향 을 미치는 심각한 문제입니다.
경고: 파티션 수 문제는 단일 테이블의 문제가 아닙니다. 클러스터 내 모든 테이블의 파티션 수 합계 가 Tablet Server 의 처리 한계를 결정합니다. 테이블 하나의 파티션을 과도하게 설계하면 다른 테이블의 성능까지 끌어내립니다.
1.4 일별 파티션 vs 월별 파티션: 파티션 단위 선택의 중요성
시계열 데이터를 다룰 때 range partition 을 일 단위 로 생성하는 것은 가장 흔한 실수 중 하나입니다. 일별 파티션이 가져오는 tablet 수 폭증을 구체적으로 비교해 보겠습니다.
| 파티션 전략 | Hash 8개 x Range 파티션 수 | 1년간 tablet 수 | 3년간 tablet 수 |
|---|---|---|---|
| 일별 파티션 | 8 x 365 | 2,920 | 8,760 |
| 월별 파티션 | 8 x 12 | 96 | 288 |
같은 테이블이라도 일별 vs 월별에 따라 30배의 tablet 수 차이 가 발생합니다. 테이블이 여러 개라면 이 차이는 배수로 커집니다.
| 시나리오 (테이블 5개, Hash 8, 3년 보관) | 일별 파티션 | 월별 파티션 |
|---|---|---|
| 클러스터 전체 tablet 수 | 43,800 | 1,440 |
| Tablet Server 5대 기준, 서버당 tablet 수 | 8,760 | 288 |
| WAL 디스크 소비 (빈 tablet 포함) | 수백 GB | 수 GB |
Tablet Server 당 권장 한계가 1,000개인 점을 감안하면, 일별 파티션 전략은 서버당 8,760개 로 권장치의 8.7배를 초과 합니다. 이는 클러스터를 불안정하게 만들고 WAL 디스크 부족으로 인한 장애를 유발합니다.
권장: 데이터를 반드시 일 단위로 조회해야 하는 경우가 아니라면, 월별 파티션을 기본 전략으로 채택 하세요. 일 단위 조회가 필요하더라도 Primary Key 에 날짜 컬럼이 포함되어 있다면 월별 파티션 내에서도 효율적인 scan pruning 이 가능합니다. 파티션 단위를 크게 잡는 것이 파티션을 잘게 쪼개는 것보다 거의 항상 안전합니다.
1.5 Range Partition 동적 관리
Range partition 은 ADD RANGE PARTITION / DROP RANGE PARTITION DDL 로 동적으로 추가/삭제할 수 있습니다. 시계열 데이터를 다룰 때는 미래 파티션을 미리 수백 개 만들어 두지 말고, 필요한 시점에 가까워지면 추가하고 오래된 파티션은 삭제 하는 전략이 효과적입니다.
-- 새 월별 파티션 추가
ALTER TABLE events ADD RANGE PARTITION VALUE = '2026-05';
-- 오래된 파티션 삭제 (해당 파티션의 모든 데이터도 함께 삭제됨)
ALTER TABLE events DROP RANGE PARTITION VALUE = '2024-01';2. 컬럼 수 제한
2.1 하드 리밋과 소프트 리밋
Kudu 는 테이블당 최대 300개 의 컬럼을 지원합니다. 하지만 이것은 하드 리밋이고, 실질적으로는 그보다 훨씬 적은 수의 컬럼을 권장합니다.
| 구분 | 컬럼 수 |
|---|---|
| 하드 리밋 | 300개 |
| 소프트 리밋 (권장) | 수십 ~ 100개 이하 |
2.2 컬럼이 많아지면 생기는 문제
- 스캔 성능 저하: Kudu 는 columnar storage 이므로 SELECT * 시 모든 컬럼의 데이터 블록을 읽어야 함. 컬럼이 많으면 I/O 양이 급증
- RowSet compaction 비용 증가: compaction 시 모든 컬럼을 읽고 다시 써야 하므로 컬럼이 많을수록 CPU, I/O 비용이 커짐
- 메타데이터 크기 증가: 스키마 정보가 모든 tablet 의 메타데이터에 포함되므로, 컬럼이 많으면 메타데이터도 커짐
- 클라이언트 측 오버헤드: Impala 등 쿼리 엔진이 스키마를 가져올 때 지연 발생
팁: 컬럼이 200개 이상 필요한 경우, 테이블을 논리적으로 분리하거나 자주 함께 조회되는 컬럼 그룹별로 나누는 것을 검토하세요.
2.3 컬럼 수 하드 리밋 강제 증가 시 Crash 위험
Kudu 의 소프트 리밋을 우회하기 위해 --max_num_columns 플래그를 사용하여 컬럼 수 제한을 하드 리밋(300개)까지 강제로 올리는 경우가 있습니다. 이는 매우 위험한 행위 이며, 다음과 같은 심각한 문제를 유발할 수 있습니다.
- Tablet Server Crash: 컬럼 수가 극단적으로 많아지면 각 tablet 의 RowSet 메타데이터, DeltaMemStore, Bloom filter 등이 비례하여 커집니다. Tablet Server 의 메모리 한계를 초과하면 OOM(Out of Memory) 으로 프로세스가 강제 종료 됩니다.
- Compaction 실패: 컬럼이 매우 많은 tablet 의 compaction 은 CPU 와 메모리를 극도로 소비합니다. Compaction 도중 메모리 부족이 발생하면 tablet 이 compaction 불가 상태 에 빠지고, 이후 쓰기 성능이 급격히 저하됩니다.
- 스키마 전파 장애: 컬럼이 수백 개인 테이블의 스키마를 모든 tablet 에 전파하는 과정에서 메타데이터 크기가 임계점을 넘으면 Master-Tablet Server 간 heartbeat 가 실패하여 tablet 이 unavailable 상태 가 될 수 있습니다.
- 복구 불가능한 상태: 최악의 경우, 대량의 컬럼으로 인해 tablet 의 메타데이터 자체가 손상되면 해당 tablet 의 데이터를 복구할 수 없습니다.
경고: 소프트 리밋은 Kudu 개발팀이 안정적인 운영을 보장할 수 있는 범위를 기준으로 설정한 것입니다.
--max_num_columns값을 올려서 하드 리밋에 근접하게 컬럼을 생성하면, Kudu 스토리지 자체가 crash 되는 치명적인 장애 가 발생할 수 있습니다. 컬럼이 많이 필요한 경우 반드시 테이블을 분리하는 방법을 선택하세요.
3. Primary Key 설계
Kudu 의 Primary Key 는 단순히 유일성을 보장하는 것이 아니라, 데이터의 물리적 저장 순서 를 결정합니다. 따라서 Primary Key 의 컬럼 구성과 순서는 성능에 직접적인 영향을 미칩니다.
3.1 Primary Key 의 특성
| 특성 | 설명 |
|---|---|
| 변경 불가 | 테이블 생성 후 Primary Key 변경 불가 (컬럼 추가/삭제/순서 변경 모두 불가) |
| NOT NULL 필수 | Primary Key 컬럼은 반드시 NOT NULL |
| 저장 순서 결정 | 데이터는 Primary Key 의 바이트 순서로 정렬 저장 |
| 인덱스 역할 | Primary Key 는 자동으로 클러스터드 인덱스로 동작 |
| 타입 제한 | FLOAT, DOUBLE, BOOL 타입은 Primary Key 로 사용 불가 |
3.2 복합 Primary Key 에서 컬럼 순서가 중요한 이유
Kudu 는 Primary Key 의 prefix 를 기반으로 스캔 범위를 pruning 합니다. 이는 RDBMS 의 복합 인덱스와 동일한 원리입니다.
예를 들어, Primary Key 가 (region, timestamp, user_id) 인 테이블에서:
| 쿼리 조건 | Pruning 가능 여부 |
|---|---|
WHERE region = 'kr' | 가능 — 첫 번째 컬럼이므로 효율적 |
WHERE region = 'kr' AND timestamp >= '2026-04-01' | 가능 — prefix 순서대로 조건 사용 |
WHERE timestamp >= '2026-04-01' | 불가능 — 첫 번째 컬럼(region) 조건이 없으므로 full scan |
WHERE user_id = 'abc' | 불가능 — prefix 를 건너뛰었으므로 full scan |
3.3 Primary Key 컬럼 순서 설계 원칙
- 가장 자주 조건으로 사용되는 컬럼을 앞에 배치: 쿼리 패턴을 분석하여 WHERE 절에 가장 자주 등장하는 컬럼을 첫 번째로
- 카디널리티 고려: 카디널리티가 너무 낮은 컬럼(예: boolean)을 첫 번째에 놓으면 pruning 효과가 미미
- 쓰기 핫스팟 방지: 단조 증가하는 값(예: timestamp)을 첫 번째 컬럼에 놓으면 모든 쓰기가 마지막 tablet 에 집중됨
- Hash Partition 과의 조합: 핫스팟 방지를 위해 첫 번째 컬럼에 hash partition 을 적용하는 것이 일반적
-- 좋은 예: hash partition 으로 쓰기 분산 + range partition 으로 시계열 관리
CREATE TABLE events (
event_id STRING NOT NULL,
event_time TIMESTAMP NOT NULL,
region STRING NOT NULL,
payload STRING,
PRIMARY KEY (event_id, event_time, region)
)
PARTITION BY
HASH (event_id) PARTITIONS 8,
RANGE (event_time) (
PARTITION VALUE = '2026-04'
)
STORED AS KUDU;-- 나쁜 예: timestamp 가 첫 번째 컬럼이면서 hash 없이 range 만 사용
CREATE TABLE events (
event_time TIMESTAMP NOT NULL,
event_id STRING NOT NULL,
region STRING NOT NULL,
payload STRING,
PRIMARY KEY (event_time, event_id, region)
)
PARTITION BY
RANGE (event_time) (
PARTITION VALUE = '2026-04'
)
STORED AS KUDU;
-- 문제: 최신 데이터 쓰기가 하나의 tablet 에 집중 → 핫스팟 발생3.4 Primary Key 컬럼 수 제한
Primary Key 를 구성하는 컬럼 수에는 명시적인 하드 리밋은 없지만, 컬럼 수가 많아지면 다음과 같은 부작용이 있습니다.
- Primary Key 인덱스 크기 증가: 각 row 의 Primary Key 바이트가 커지면 인덱스가 메모리와 디스크를 더 많이 차지
- 비교 연산 비용: Insert/Update/Delete 시 Primary Key 비교 비용이 증가
- 스키마 유연성 감소: Primary Key 컬럼은 변경 불가이므로, 많이 포함할수록 스키마 변경의 여지가 줄어듦
권장: Primary Key 는 3~5개 이하의 컬럼 으로 구성하되, 반드시 쿼리 패턴과 파티셔닝 전략을 함께 고려하세요.
4. 기타 주요 제약 사항
4.1 데이터 타입 관련
| 제약 | 설명 |
|---|---|
DECIMAL 정밀도 | 최대 precision 38, scale 38. 금융 데이터 처리 시 주의 |
VARCHAR / STRING 최대 크기 | 셀 단위 최대 64 KB |
BINARY 최대 크기 | 셀 단위 최대 64 KB |
| Primary Key 의 셀 크기 합계 | 최대 16 KB |
| immutable 컬럼 | Primary Key 컬럼의 값은 INSERT 후 변경 불가 |
4.2 스키마 변경 관련
| 제약 | 설명 |
|---|---|
| Primary Key 변경 | 불가능. 스키마 변경으로 PK 를 수정하거나 컬럼을 추가/삭제할 수 없음 |
| 컬럼 타입 변경 | 불가능. 기존 컬럼의 데이터 타입을 변경할 수 없음 |
| 컬럼 이름 변경 | 가능 |
| 컬럼 추가/삭제 | 가능 (단, Primary Key 컬럼 제외) |
| 파티션 스키마 변경 | 불가능. Hash partition 의 bucket 수나 partition 컬럼 자체를 변경할 수 없음 |
| Range partition 추가/삭제 | 가능. 동적으로 범위 파티션 추가/삭제 가능 |
4.3 복제(Replication) 관련
| 제약 | 설명 |
|---|---|
| Replication Factor | 테이블 생성 시 지정, 이후 변경 불가. 홀수만 가능 (기본값 3) |
| 최소 Tablet Server 수 | Replication Factor 이상의 Tablet Server 필요 |
4.4 Secondary Index
Kudu 는 secondary index 를 지원하지 않습니다. 따라서 Primary Key 가 아닌 컬럼으로 조회할 때는 full scan 이 발생합니다. Impala 와 함께 사용할 경우 Impala 의 실행 계획에 의존하게 되므로, Primary Key 와 파티셔닝 전략을 신중하게 설계해야 합니다.
4.5 멀티 로우(Multi-row) 트랜잭션
Kudu 는 단일 row 수준의 원자성(atomicity) 만 보장합니다. 여러 row 에 걸친 트랜잭션은 지원하지 않으므로, 여러 row 를 동시에 업데이트해야 하는 비즈니스 로직은 애플리케이션 레벨에서 보정해야 합니다.
4.6 테이블 수 제한
클러스터 전체에서 관리하는 테이블 수에는 명시적인 하드 리밋은 없지만, 테이블이 늘어나면 각 테이블의 tablet 수가 합산되므로 결과적으로 tablet 수 제한에 걸립니다. 테이블 수 x 테이블당 tablet 수 = 클러스터 전체 tablet 수 가 핵심 공식입니다.
5. 설계 체크리스트
테이블을 생성하기 전에 다음 항목을 확인하세요.
파티셔닝
- Hash partition bucket 수가 Tablet Server 수의 배수에 가까운가?
- Range partition 을 사용한다면 미래 파티션을 과도하게 미리 생성하지 않았는가?
- 테이블당 전체 tablet 수 = Hash bucket 수 x Range partition 수 가 적정한가?
- Tablet Server 당 총 tablet 수가 1,000개를 넘지 않는가?
- 빈 tablet 으로 인한 디스크 낭비가 허용 범위인가?
- WAL 디스크 용량이 전체 tablet 수를 감당할 수 있는가?
- 일별 파티션 대신 월별 파티션을 사용할 수 있는지 검토했는가?
- 클러스터 내 모든 테이블의 파티션 합계를 계산했는가?
컬럼 설계
- 컬럼 수가 100개를 넘지 않는가? (넘는다면 테이블 분리 검토)
--max_num_columns플래그를 강제로 올리지 않았는가? (crash 위험)- 불필요한 컬럼이 포함되지 않았는가?
- STRING/BINARY 컬럼에 64 KB 를 초과하는 데이터가 들어올 가능성은 없는가?
Primary Key
- Primary Key 컬럼 순서가 주요 쿼리 패턴과 일치하는가?
- 단조 증가하는 값이 Primary Key 첫 번째 컬럼에 올 경우 Hash partition 을 적용했는가?
- Primary Key 에 불필요하게 많은 컬럼을 넣지 않았는가?
- Primary Key 셀 크기 합계가 16 KB 이하인가?
- Primary Key 컬럼에 FLOAT, DOUBLE, BOOL 타입을 사용하지 않았는가?
운영
- Replication Factor 를 적절히 설정했는가? (운영 환경에서는 3 권장)
- 스키마 변경이 필요해질 가능성이 있다면 Primary Key 를 최소한으로 설계했는가?
- Secondary Index 가 없다는 점을 고려하여 조회 패턴에 맞는 PK 를 구성했는가?
6. 정리
| 항목 | 제약 / 권장 |
|---|---|
| Tablet Server 당 tablet 수 | 1,000개 이하 권장 |
| 빈 tablet 디스크 사용량 | tablet 당 수 MB (WAL 포함) |
| WAL 디스크 | 파티션 수에 비례하여 증가 — 용량 산정 시 반드시 고려 |
| 파티션 단위 권장 | 일별보다 월별 파티션 우선 검토 |
| 컬럼 수 하드 리밋 강제 증가 | Tablet Server crash 위험 — 절대 비권장 |
| 테이블당 최대 컬럼 수 | 300개 (권장 100개 이하) |
| Primary Key 변경 | 불가능 |
| Primary Key 컬럼 타입 제한 | FLOAT, DOUBLE, BOOL 사용 불가 |
| Primary Key 셀 크기 합계 | 16 KB 이하 |
| STRING/BINARY 셀 최대 크기 | 64 KB |
| Secondary Index | 미지원 |
| 멀티 로우 트랜잭션 | 미지원 (단일 row 원자성만 보장) |
| Range Partition 동적 관리 | 지원 (ADD / DROP) |
| Hash Partition 변경 | 불가능 |
| Replication Factor 변경 | 불가능 |
Kudu 는 설계 시점에 내린 결정이 운영 내내 영향을 미치는 스토리지 엔진입니다. 특히 Primary Key 와 파티셔닝 스키마는 한 번 정하면 바꿀 수 없기 때문에, 테이블을 만들기 전에 쿼리 패턴, 데이터 증가율, 클러스터 규모를 충분히 고려해야 합니다. "나중에 고치면 되지" 라는 접근은 Kudu 에서는 통하지 않습니다.
이 글은 Apache Kudu 공식 Known Issues and Limitations 와 Schema Design Guide 를 참고하여 작성되었습니다. Kudu 테이블 설계나 운영에 대해 도움이 필요하시면 언제든 문의해 주세요.
— Data Dynamics 엔지니어링 팀