Hive ACID ORC 테이블 Compaction 가이드
delta 디렉터리가 쌓이는 Hive ACID(Transactional) ORC 테이블을 Major Compaction, Rebalance Compaction, Hive INSERT OVERWRITE로 정리하고 결과 파일 수까지 제어하는 방법을 정리했습니다.
Hive ACID(Transactional) ORC 테이블은 INSERT·UPDATE·DELETE가 실행될 때마다 Partition 아래에 delta_* 디렉터리를 하나씩 만듭니다. 적재가 잦은 테이블에서는 Partition 하나에 수백 개의 delta가 쌓이고, 이것이 ACID 테이블에서 Small File이 생기는 원인입니다.
Non-ACID 테이블은 PySpark로 Staging을 거쳐 다시 쓰는 방식을 썼지만, ACID 테이블은 Spark로 직접 병합하면 안 됩니다. 이 글은 Hive 자체 기능으로 ACID 테이블을 Compaction하고, 결과 파일 수까지 제어하는 방법을 다룹니다. 원본 문서는 DataDynamics/hive-orc-table-compaction 저장소의 ACID_COMPACTION.md입니다.
ACID 테이블 판별
테이블 속성
SHOW TBLPROPERTIES dw.sales('transactional'); -- true면 ACID
SHOW TBLPROPERTIES dw.sales('transactional_properties');transactional_properties | 의미 |
|---|---|
없음 또는 default | Full ACID (INSERT / UPDATE / DELETE / MERGE) |
insert_only | Insert-only ACID (MM 테이블) |
HDP 3.x / CDP처럼 Managed Table이 기본적으로 ACID로 생성되는 환경에서는 별도로 지정하지 않아도 ACID 테이블일 수 있습니다.
HDFS 디렉터리 구조
Partition 경로에 다음과 같은 디렉터리가 있다면 ACID 테이블입니다.
/warehouse/tablespace/managed/hive/dw.db/sales/dt=2026-10-01/
├── base_0000123/
│ ├── bucket_00000
│ ├── bucket_00001
│ └── ...
├── delta_0000124_0000124_0000/
│ └── bucket_00000
├── delta_0000125_0000125_0000/
│ └── bucket_00000
└── delete_delta_0000126_0000126_0000/
└── bucket_00000| 디렉터리 / 파일 | 의미 |
|---|---|
base_N | Major Compaction 또는 INSERT OVERWRITE 결과 |
delta_N_M_S | INSERT / UPDATE 1회마다 생성되는 변경분 |
delete_delta_N_M_S | UPDATE / DELETE로 삭제된 Row 정보 |
bucket_NNNNN | ACID 데이터 파일. CLUSTERED BY가 없는 테이블에도 이 이름이 쓰입니다 |
반복 INSERT마다 delta_*가 하나씩 쌓이는 구조입니다.
Spark로 병합하면 안 되는 이유
- Spark는 Hive Warehouse Connector(HWC) 없이
base/delta/delete_delta를 병합해서 읽지 못합니다. Row Count와 데이터가 틀리거나 읽기 자체가 실패할 수 있습니다. - Spark로
INSERT OVERWRITE하면 Hive의 Transaction 메타데이터(Write ID)와 실제 파일이 어긋날 수 있습니다. - Non-ACID 글의 PySpark 프로그램도
transactional=true이면 실행을 중단하도록 되어 있습니다.
따라서 ACID 테이블은 Hive 자체 기능으로 Compaction합니다.
Minor와 Major Compaction
| 종류 | 동작 | 결과 |
|---|---|---|
| Minor | 여러 delta를 하나의 delta로, 여러 delete_delta를 하나의 delete_delta로 병합 | delta_124_130 |
| Major | base + 모든 delta + delete_delta를 하나의 새로운 base로 재작성 | base_130 |
Small File 정리가 목적이라면 Major Compaction을 사용합니다.
Major Compaction 실행
beeline에서 실행합니다.
-- 1. ACID 여부 확인
SHOW TBLPROPERTIES dw.sales('transactional');
-- 2. Partition 단위로 Major Compaction 요청
ALTER TABLE dw.sales
PARTITION (dt='2026-10-01')
COMPACT 'major';
-- 3. 진행 상태 확인
SHOW COMPACTIONS;ALTER TABLE ... COMPACT는 요청을 Queue에 등록만 하고 바로 반환합니다. 완료될 때까지 기다리려면 AND WAIT을 붙입니다. 배치 스크립트에서 다음 단계(검증)로 넘어가기 전에 유용합니다.
ALTER TABLE dw.sales
PARTITION (dt='2026-10-01')
COMPACT 'major' AND WAIT;SHOW COMPACTIONS의 상태는 다음 순서로 바뀝니다.
| 상태 | 의미 |
|---|---|
initiated | Queue에 등록됨 |
working | Compactor Worker가 처리 중 |
ready for cleaning | 새로운 base 생성 완료, 이전 디렉터리 삭제 대기 |
succeeded | Cleaner가 이전 디렉터리 삭제 완료 |
failed | 실패. Metastore / HiveServer2 로그 확인 필요 |
이전 base / delta 디렉터리는 그 디렉터리를 읽고 있는 쿼리가 모두 끝난 뒤에 Cleaner가 삭제합니다. 따라서 Compaction 직후에도 HDFS에 한동안 남아 있을 수 있습니다.
Compaction이 initiated에서 진행되지 않는다면 Hive Metastore의 다음 설정을 확인합니다.
| 설정 | 필요한 값 |
|---|---|
hive.compactor.initiator.on | true |
hive.compactor.worker.threads | 1 이상 |
결과 파일 개수와 크기 제어
여기가 ACID 테이블 Compaction에서 가장 자주 오해하는 부분입니다. COMPACT 'major' 자체에는 결과 파일 개수나 크기를 지정하는 옵션이 없습니다. Major Compaction 이후 base_N 안의 파일 수는 다음과 같이 정해집니다.
| 테이블 유형 | Major Compaction 이후 파일 수 |
|---|---|
Bucketed ACID (CLUSTERED BY ... INTO N BUCKETS) | N개 고정 (Bucket당 1개) |
| Non-bucketed ACID | 입력 데이터에 존재하는 Bucket ID 종류 수 (최초 적재 시 Writer Task 수로 결정) |
orc.stripe.size 같은 ORC 설정은 파일 내부 Stripe 크기에만 영향을 줄 뿐, 파일 개수나 크기를 결정하지 않습니다. 파일 수를 직접 제어하려면 다음 두 방법 중 하나를 씁니다.
방법 1: Rebalance Compaction (Hive 4.x)
Hive 4.0부터는 Non-bucketed Full ACID 테이블에 대해 결과 Bucket(파일) 수를 지정해 Compaction할 수 있습니다.
ALTER TABLE dw.sales
PARTITION (dt='2026-10-01')
COMPACT 'rebalance'
CLUSTERED INTO 8 BUCKETS;- Query-based Compaction이 활성화되어 있어야 합니다.
- Bucketed 테이블과 Insert-only 테이블에는 사용할 수 없습니다.
- Hive 3.x / HDP 3.x에는 이 구문이 없으며, CDP는 배포 버전에 따라 지원 여부가 다릅니다.
- 사용 전
SELECT version();으로 Hive 버전을 확인하고, 사용하는 배포판 문서에서 구문을 확인하십시오.
방법 2: Hive INSERT OVERWRITE (권장, 버전 무관)
Hive(beeline)에서 같은 Partition을 다시 씁니다. 결과 파일 수가 Reducer 수와 같으므로 정확하게 제어할 수 있습니다.
목표 파일 수는 Partition 크기로 계산합니다.
Target Files = ceil(Partition Size / Target File Size)
예) 4 GB / 512 MB = 4 × 1024 / 512 = 8hdfs dfs -du -s -h /warehouse/tablespace/managed/hive/dw.db/sales/dt=2026-10-01파일 개수 고정
SET hive.tez.auto.reducer.parallelism=false;
SET mapreduce.job.reduces=8;
INSERT OVERWRITE TABLE dw.sales
PARTITION (dt='2026-10-01')
SELECT
id,
customer_id,
amount
FROM dw.sales
WHERE dt='2026-10-01'
DISTRIBUTE BY pmod(hash(id), 8);| 설정 | 역할 |
|---|---|
hive.tez.auto.reducer.parallelism=false | Tez가 Reducer 수를 자동으로 줄이지 않도록 고정 |
mapreduce.job.reduces=8 | Reducer 수 = 결과 파일 수 |
DISTRIBUTE BY pmod(hash(id), 8) | 8개 Reducer에 Row를 고르게 분배 |
DISTRIBUTE BY rand()는 쓰지 마십시오. Task가 재시도되면 같은 Row가 다른 Reducer로 가서 중복되거나 누락될 수 있습니다. 분배 Key는 값이 고르게 분포된 결정적 컬럼(예: PK)을 사용합니다.
크기 기준
SET hive.exec.reducers.bytes.per.reducer=536870912; -- 512 MB이 값은 입력 데이터 크기로 Reducer 수를 추정하므로 결과 파일 크기는 근사치입니다. 정확한 개수가 필요하면 위의 파일 개수 고정 방식을 씁니다.
주의 사항
- ACID 테이블은 Snapshot Isolation을 지원하므로 같은 Partition을 읽으면서 Overwrite할 수 있습니다. Non-ACID에서 Staging이 필요했던 것과 다른 점입니다. 결과로 새로운
base_N이 생성되고, 이전delta는 Cleaner가 정리합니다. - 실행 중 Partition에 Exclusive Lock이 걸리므로 적재가 완료된 Partition(일반적으로 D-1 이전)에만 실행합니다.
SELECT컬럼 목록에는 Partition Column을 제외한 모든 컬럼을 정의 순서대로 지정해야 합니다.
Row Count 검증
Compaction 또는 INSERT OVERWRITE 전후로 건수를 비교합니다. 이때 hive.compute.query.using.stats=true이면 COUNT(*)가 실제 데이터가 아니라 Metastore 통계로 응답될 수 있으므로 검증 시에는 꺼 둡니다.
SET hive.compute.query.using.stats=false;
-- 실행 전
SELECT COUNT(*) FROM dw.sales WHERE dt='2026-10-01';
-- Compaction / INSERT OVERWRITE 실행
-- 실행 후
SELECT COUNT(*) FROM dw.sales WHERE dt='2026-10-01';결과 파일 수는 HDFS에서 확인합니다.
hdfs dfs -ls -R /warehouse/tablespace/managed/hive/dw.db/sales/dt=2026-10-01자동 Compaction 기준
Hive Metastore의 Initiator는 다음 기준으로 Compaction을 자동 요청합니다. 이 설정들은 결과 파일 크기가 아니라 언제 Compaction을 시작할지를 결정합니다.
| 설정 | 의미 | 기본값 |
|---|---|---|
hive.compactor.delta.num.threshold | delta 디렉터리 수가 이 값을 넘으면 Minor Compaction | 10 |
hive.compactor.delta.pct.threshold | delta 크기가 base 대비 이 비율을 넘으면 Major Compaction | 0.1 |
hive.compactor.check.interval | Initiator가 대상을 확인하는 주기 | 300s |
특정 테이블만 자동 Compaction을 끄려면 다음과 같이 설정합니다. 위의 INSERT OVERWRITE 방식으로 파일 수를 직접 관리하는 테이블에 유용합니다.
ALTER TABLE dw.sales SET TBLPROPERTIES ('no_auto_compaction'='true');Small File 예방
Compaction보다 Small File이 덜 생기도록 적재 방식을 바꾸는 편이 효과적입니다.
- 작은
INSERT를 여러 번 수행하지 말고 가능한 한 모아서 한 번에 적재합니다. INSERT INTO ... VALUES를 반복 실행하지 않습니다. 실행할 때마다delta가 하나씩 생깁니다.- 적재가 끝난 Partition은 정기적으로 Major Compaction을 수행합니다.
운영 흐름
권장 운영 값
| 항목 | 권장 |
|---|---|
| Compaction 단위 | Hive Partition |
| 기본 방법 | COMPACT 'major' |
| 파일 수 제어 | Hive INSERT OVERWRITE + Reducer 수 지정 (Hive 4.x는 Rebalance Compaction) |
| 목표 파일 크기 | 256 MB ~ 512 MB |
| 대상 | 적재 완료 Partition (D-1 이전) |
| 검증 | 전후 COUNT(*) (hive.compute.query.using.stats=false) |
| Spark 직접 병합 | 사용 금지 |
정리
| 구분 | Non-ACID | ACID |
|---|---|---|
| Small File 원인 | 반복 INSERT마다 생기는 작은 ORC 파일 | 반복 INSERT·UPDATE·DELETE마다 생기는 delta_* |
| Hive Compactor | 동작하지 않음 | 자동(Initiator) 또는 COMPACT 요청 |
| 권장 방법 | PySpark + Staging + INSERT OVERWRITE | COMPACT 'major' |
| 파일 수 제어 | 목표 크기로 coalesce | Hive INSERT OVERWRITE + Reducer 수, 또는 Rebalance (Hive 4.x) |
| 같은 Partition 읽으며 Overwrite | 불가 (Staging 필요) | 가능 (Snapshot Isolation) |
| Spark 사용 | 가능 | 금지 (HWC 없이) |
ACID 테이블의 기본은 COMPACT 'major'이고, 파일 수까지 맞춰야 할 때만 Hive INSERT OVERWRITE나 Rebalance Compaction을 씁니다. 어떤 방법이든 적재가 끝난 Partition에만, 통계 응답을 끈 COUNT(*)로 전후 검증하는 것이 운영의 기본입니다.