요약
Parquet 파일에 저장되는 FLOAT·DOUBLE 컬럼은 오랫동안 압축에 취약한 영역이었다. 카디널리티가 낮으면 RLE_DICTIONARY가 작동하지만, 센서 값·지표·가격 같은 컬럼은 값 자체가 너무 다양해 딕셔너리가 거의 효과를 내지 못한다. 남은 선택지는 PLAIN(비압축)이나 ZSTD 같은 범용 바이트 압축인데, ZSTD는 CPU를 많이 쓴다.
2024년 SIGMOD에서 CWI의 Azim Afroozeh와 Peter Boncz가 발표한 ALP(Adaptive Lossless floating-Point Compression)는 이 문제를 다른 방향으로 접근한다. 부동소수점 수의 10진수 구조를 이용해 "의사소수점(PseudoDecimal)" 경로와 "프런트비트 벡터화" 경로 중 하나를 값 집합에 맞게 자동 선택해, ZSTD와 유사한 압축률을 내면서 압축 해제 속도는 1~2자릿수 더 빠른 수준을 달성한다.
2026년 7월 29일, ALP 인코딩이 parquet-format 스펙에 공식 병합됐다. 인코딩 식별자 값은 10(ADAPTIVE_LOSSLESS_FLOATING_POINT)이다.
- 적용 대상:
FLOAT(FLOAT32),DOUBLE(FLOAT64) 컬럼 - 기본 벡터 크기: 1,024개 값(8~32,768 설정 가능)
- 페이지당 헤더 오버헤드: 7바이트
- SIGMOD 2024 논문 (Afroozeh & Boncz, CWI Amsterdam)
- DuckDB에 조기 구현 포함, Arrow/parquet-cpp 구현 진행 중
배경: Parquet 부동소수점 압축의 공백
기존 Parquet 인코딩의 한계
Parquet는 컬럼 유형에 따라 여러 인코딩을 제공한다. 정수형은 DELTA_BINARY_PACKED, 문자열은 RLE_DICTIONARY가 보통 효과적이다. 그러나 부동소수점 컬럼은 선택지가 빈약했다.
| 인코딩 | 특성 | 부동소수점 효과 |
|---|---|---|
| PLAIN | 비압축 원본 저장 | 없음 |
| RLE_DICTIONARY | 값 사전 공유 | 카디널리티 낮을 때만 효과적 |
| BYTE_STREAM_SPLIT | 바이트 레인 분리 | 압축률 낮음, 범용 압축기 보조 목적 |
| DELTA_BINARY_PACKED | 정수 델타 | 부동소수점 적용 불가 |
실제로 시계열 측정값, 금융 지표, 센서 데이터 같은 컬럼은 값마다 다르면서 IEEE 754 표현 패턴을 공유한다. 사람이 읽는 소수 표현(12.34, 0.0057, 99.9)이 내부적으로는 지수·가수·부호 비트로 분산되어 있어, 바이트 단위 범용 압축이 잘 작동하지 않는다.
ALP의 출발점: 부동소수점 수의 숨은 구조
10진수로 표현 가능한 부동소수점 수(f = i × 10^-d, 여기서 i와 d는 정수)는 변환하면 작은 정수 배열이 된다. 이 정수는 DELTA_BINARY_PACKED으로 효율적으로 압축할 수 있다.
1.23 → i=123, d=2
45.0 → i=4500, d=2
0.007 → i=7, d=3모든 부동소수점 수가 이 구조를 갖지는 않는다. ALP는 이를 인정하고 두 가지 경로로 처리한다.
ALP 알고리즘: 두 가지 경로
경로 A: PseudoDecimal (ALP-Decimals)
센서 값, 환율, 측정 지표처럼 10진수로 표현 가능한 부동소수점 수에 사용된다.
- 지수 추출: 벡터 내 값들에서 최적 지수
d를 하나 선택한다. - 정수 변환: 각 값
f를i = round(f × 10^d)로 변환한다. - 비트팩: 변환된 정수 배열을 DELTA 인코딩 + 비트팩으로 압축한다.
- 예외 처리: 정수로 변환되지 않는 값(정밀도 손실 발생 가능성 있는 값)은 예외 목록에 원본 비트 그대로 기록한다.
전체 과정이 SIMD(AVX-512 등) 명령어로 벡터화된다.
경로 B: Front-bit Vectorized (ALP-RD)
과학적 계산 결과, 무작위 시뮬레이션 값처럼 10진수 구조가 없는 부동소수점 수에 사용된다.
- 비트 분리: IEEE 754 비트 표현을 앞부분(지수+부호)과 뒷부분(가수 하위 비트)으로 나눈다.
- 앞부분 압축: 지수 비트는 벡터 내에서 공통 패턴을 공유하는 경우가 많아 델타 인코딩으로 줄인다.
- 뒷부분 비트팩: 가수 하위 비트를 폭(width)을 측정해 최소 비트로 팩한다.
Parquet 포맷 통합 (2026년 7월 29일)
스펙 변경 내용
2026년 7월 29일 parquet-format 레포지터리에 ALP 인코딩이 공식 병합됐다. 핵심 변경 사항은 다음과 같다.
| 항목 | 값 |
|---|---|
| 인코딩 식별자 | 10 (ADAPTIVE_LOSSLESS_FLOATING_POINT) |
| 적용 가능 타입 | FLOAT (FLOAT32), DOUBLE (FLOAT64) |
| 페이지 헤더 추가 오버헤드 | 7바이트 |
| 기본 벡터 크기 | 1,024개 값 |
| 최소·최대 벡터 크기 | 8 ~ 32,768개 |
ALP 페이지 헤더 구조
각 데이터 페이지 앞에 7바이트 ALP 헤더가 붙는다.
[1바이트] 경로 플래그 (0 = PseudoDecimal, 1 = Front-bit)
[1바이트] 지수(exponent) 값 d (PseudoDecimal 경로에서만 의미 있음)
[2바이트] 예외 개수 (exception count)
[2바이트] 예외 비트 폭 (exception bit width)
[1바이트] 비트팩 폭 (bitpack width)경로는 벡터 단위로 독립 선택된다. 같은 컬럼 내에서도 페이지마다 경로가 달라질 수 있다.
압축률과 속도 비교
압축률: ZSTD와 유사, PLAIN보다 크게 우수
아래 수치는 SIGMOD 2024 논문 기준이며, 데이터셋에 따라 달라질 수 있다.
| 인코딩 | 압축률 | 압축 해제 속도 | 특이사항 |
|---|---|---|---|
| PLAIN | 1.0× (기준) | 매우 빠름 | 압축 없음 |
| BYTE_STREAM_SPLIT + ZSTD | 2.5~4× | 느림 | CPU 부담 큼 |
| ALP (PseudoDecimal) | 2~6× | 매우 빠름 | SIMD 벡터화 |
| ALP (Front-bit) | 1.5~3× | 빠름 | 비정형 값 처리 |
| Gorilla (시계열 전용) | 2~5× | 빠름 | 시계열에만 최적 |
ALP가 ZSTD와 비슷한 압축률을 내면서도 압축 해제 속도가 1~2자릿수(10~100배) 빠른 이유는, ZSTD가 바이트 수준의 엔트로피 코딩(Huffman, ANS)을 쓰는 반면 ALP는 SIMD 정수 연산만으로 처리하기 때문이다.
어떤 컬럼에 효과적인가
• 센서·IoT 측정값
• 금융 가격·환율
• 의료 수치 데이터
• 로그 지표(latency ms)
• 머신러닝 가중치
• 물리 시뮬레이션
• GPS 좌표 (정밀 소수)
• 집계 통계 중간값
• NaN / Inf 비율 높음
• 랜덤 해시 기반 값
• 균등 분포 실수
• 이미 ZSTD 압축됨
운영 시사점
언제 ALP를 켜야 하는가
ALP는 모든 FLOAT/DOUBLE 컬럼에 자동으로 최적인 것은 아니다. 스펙에 추가됐다는 것은 라이브러리가 지원을 시작한다는 의미이지, 기본값이 바뀐다는 뜻이 아니다.
켜면 좋은 경우:
- 컬럼값이 사람이 쓰거나 측정한 소수(소수점 자리수가 제한적)
- 쿼리가 분석 패턴(집계, 필터)이고 압축 해제 속도가 중요한 경우
- 스토리지 비용 절감이 목표이고 CPU 여유가 있는 경우
주의가 필요한 경우:
- 모델 가중치나 임베딩 벡터: 경로 B가 적용되지만 압축률이 낮을 수 있다
- 레거시 리더 호환성: 인코딩 값 10을 인식하지 못하는 구 버전 라이브러리는 읽기 실패
라이브러리 지원 현황 (2026년 8월 기준)
| 라이브러리 | 상태 |
|---|---|
| DuckDB | 쓰기·읽기 지원 |
| parquet-cpp (Arrow) | 구현 진행 중 |
| parquet-go | 논의 중 |
| Spark parquet 모듈 | 지원 계획 미정 |
| pandas (PyArrow 백엔드) | Arrow 지원 후 연동 예정 |
스펙 병합이 먼저이고, 각 라이브러리의 실제 구현은 별도 일정을 따른다. 새 인코딩을 쓰려면 쓰기 라이브러리가 지원해야 하고, 읽기 라이브러리도 인코딩 식별자 10을 인식해야 한다.
도입 체크리스트
- [ ] 쓰기 라이브러리 버전이 ALP 인코딩을 지원하는지 확인
- [ ] 파이프라인 내 모든 리더(Spark, Trino, Athena 등)가
encoding=10을 처리할 수 있는지 검증 - [ ] 혼용 컬럼(정상 소수 + 간헐적 NaN)의 예외 비율을 샘플로 측정
- [ ] 기존 PLAIN/ZSTD 파일과 신규 ALP 파일의 크기·읽기 시간 A/B 비교
- [ ] 롤백 경로: ALP 파일을 인식 못 하는 구 리더를 위한 PLAIN 폴백 파일 보관 전략 수립
요점 정리
- ALP = 인코딩, 압축 알고리즘 아님: ALP는 Parquet 인코딩 레이어에서 동작한다. ZSTD·LZ4 같은 페이지 수준 압축과 독립적으로, 그 전에 데이터를 더 압축 친화적인 형태로 변환한다.
- 두 경로 자동 선택: PseudoDecimal(10진수 구조 활용)과 Front-bit(비트 분리)를 벡터마다 자동으로 고른다. 사용자가 경로를 지정할 필요 없다.
- SIGMOD 2024 → 스펙 병합 2026: 논문 발표 후 약 2년 만에 표준 스펙에 들어왔다. 라이브러리 구현은 순차 진행 중이다.
- 인코딩 식별자 10: Parquet 파일 메타데이터에서
encoding=10으로 표시된다. - DuckDB가 선도: DuckDB는 조기부터 ALP를 구현해 실제 성능 데이터를 축적했다. Arrow/parquet-cpp 지원이 완료되면 다른 엔진도 빠르게 적용될 가능성이 높다.
References
- Afroozeh, A., & Boncz, P. (2024). ALP: Adaptive Lossless floating-Point Compression. Proceedings of the ACM SIGMOD International Conference on Management of Data. https://dl.acm.org/doi/10.1145/3626717
- Apache Parquet Format Specification — Encodings. https://parquet.apache.org/docs/file-format/data-pages/encodings/
- parquet-format GitHub repository — ADAPTIVE_LOSSLESS_FLOATING_POINT (encoding value 10). https://github.com/apache/parquet-format
- ALP reference implementation (CWI). https://github.com/cwida/ALP
- DuckDB Parquet ALP support. https://github.com/duckdb/duckdb