lakehouse은(는) 무엇을 하나요?
오픈 테이블 포맷 스토리지 및 Spark 코드를 위한 레퍼런스급 결정 지원 도구입니다. 버전 및 날짜 정보는 references/landscape-2026-11.md에 있습니다; 버전 관련 주장 전에 반드시 확인하세요. Databricks, Snowflake 또는 AWS 서비스별 세부 사항은 de-vendors 스킬과 해당 공식 벤더 스킬을 사용하세요.
1. 레이크하우스가 필요한가?
가장 저렴한 옵션부터 시작하여 실제로 측정된 트리거가 있을 때만 상위 옵션으로 이동하세요.
- Postgres (또는 단일 파일 세트의 DuckDB): 데이터가 약 1TB 미만이고, 하나의 엔진만 읽으며, 동시 분석 사용자가 적은 경우. 이유: 카탈로그, 압축, 실행 클러스터가 필요 없음.
- 클라우드 웨어하우스: 많은 SQL 사용자가 탄력적인 동시성을 필요로 하고 같은 데이터에 다른 엔진이 필요하지 않은 경우. 이유: 관리형 스토리지, 유지보수 및 거버넌스가 가격에 포함됨.
- 레이크하우스: 다음 중 하나라도 해당될 때: 수십 TB 이상; 여러 엔진(Spark, Trino, DuckDB, Python)이 동일한 복사본을 읽어야 함; ML 또는 파일 기반 워크로드가 SQL과 테이블을 공유; 오브젝트 스토리지 비용이 귀하의 볼륨에서 웨어하우스 스토리지보다 명확히 저렴함; 오픈 포맷이 필수 조건(탈퇴, 감사, 벤더 중립성).
- 숫자가 없는 "미래 규모"를 위해 레이크하우스를 선택하지 마세요. 추천 전에 행 수, 일일 증가량, 동시 판독기 및 엔진을 물어보세요.
- 오픈 포맷을 원하는 소규모 및 중간 규모 데이터: DuckLake 또는 일반 Parquet + DuckDB가 Spark를 실행하는 것보다 낫습니다.
2. Parquet 레이아웃
- 파일당 128MB ~ 1GB를 목표로 하며, 확실하지 않으면 256MB ~ 512MB를 목표로 하세요. 32MB 미만의 파일이 많으면 목록 및 열기 비용이 쿼리를 지배합니다.
- 파일당 약 128MB 압축되지 않은(1M+ 행) 행 그룹; 더 작은 그룹은 min/max 조건 제거 및 압축을 약화시킵니다.
- 쿼리가 필터링하는 낮은 카디널리티 열(날짜 또는 월, 지역)로만 파티션하세요. 좋은 파티션은 작성자 배치당 최소 하나의 대상 크기 파일을 포함해야 합니다; 파티션이 평균 1GB 미만이면 더 거친 입자 또는 클러스터링을 대신 사용하세요.
- 높은 카디널리티 ID로 파티션하지 마세요. 해당 열에는 클러스터링(Delta 리퀴드 클러스터링, Iceberg 정렬 순서/Z-order)을 사용하세요.
- 파일 내에서 가장 많이 필터링된 열로 정렬 또는 클러스터링하여 min/max 통계가 행 그룹을 제거할 수 있게 하세요.
- 스트리밍 또는 빈번한 마이크로 배치 쓰기는 작은 파일을 생성합니다; 압축을 예약하고, 작성자가 파일 크기를 조정하는 데 의존하지 마세요.
3. 테이블 포맷 선택
기능 목록이 아닌 이미 보유한 엔진과 카탈로그에 따라 결정하세요.
- Delta Lake: Databricks 또는 Spark가 기본 엔진이거나 Unity Catalog가 이미 거버넌스 계층인 경우 선택하세요. 가장 강력한 Spark 통합; 다른 엔진이 읽을 수 있지만 쓰기 지원은 다양합니다.
- Apache Iceberg: 여러 엔진이 쓰기해야 하거나 벤더 중립적 카탈로그(REST)를 원하는 경우 선택하세요. v3는 삭제 벡터, 행 계보 및 변형 유형을 추가합니다; 테이블을 업그레이드하기 전에 경로의 모든 엔진과 카탈로그가 사용하는 v3 기능을 지원하는지 확인하세요. 이전 리더는 최신 포맷 버전에서 실패하기 때문입니다.
- DuckLake: 소규모 및 중간 규모 데이터, 단일 팀, 그리고 이미 운영 중인 SQL 데이터베이스를 카탈로그로 사용하는 경우 선택하세요(메타데이터는 일반 테이블에 저장). 실행 비용이 저렴하고 생태계가 젊습니다; Spark, Trino 또는 Snowflake가 동일한 테이블을 써야 하는 경우 선택하지 마세요.
- 상호 운용성 계층(Delta UniForm, Delta 테이블용 Iceberg 메타데이터)은 읽기 경로입니다. 테이블당 하나의 포맷을 기록 작성자로 선택하세요.
- 나중에 포맷을 변경하려면 다시 쓰기 또는 메타데이터 변환이 필요합니다; 의도적으로 기록 작성자를 선택하세요.
4. 카탈로그
- 카탈로그는 커밋 코디네이터이자 액세스 제어 지점입니다; 포맷 세부 사항보다 먼저 선택하세요.
- Unity Catalog: Databricks 중심 기본값; Delta 및 Iceberg를 위한 거버넌스.
- AWS Glue 및 S3 Tables: 스택이 AWS 네이티브인 경우 선택하세요; S3 Tables는 압축을 자동으로 관리하지만 AWS에 종속됩니다.
- Iceberg REST 카탈로그(예: Polaris): 다중 엔진, 다중 클라우드 중립성을 위해 선택하세요.
- DuckLake: 카탈로그는 SQL 데이터베이스입니다; 다중 사용자 사용을 위해 Postgres로 백업하고, 단일 작성자에 대해서만 SQLite 또는 DuckDB를 사용하세요.
- 두 카탈로그가 동일한 테이블 경로를 등록하지 마세요; 하나에 대한 커밋이 다른 쪽의 보기를 손상시킵니다.
5. 유지보수 (예약하지 않으면 테이블이 저하됨)
- 작은 파일 압축: Delta
OPTIMIZE, Icebergrewrite_data_files, DuckLake 병합-인접-파일. 대규모 수집 후에 실행하고 쓰기마다 실행하지 마세요. - 오래된 데이터 제거: Delta
VACUUM, Icebergexpire_snapshots+ 고아 파일 제거, DuckLake 스냅샷 만료 후 정리. 보존 기간은 가장 오래된 리더 또는 타임 트래블 창보다 길어야 합니다. 실행 중인 리더 아래에서 파일을 삭제하면 중단되기 때문입니다. - 클러스터링 또는 Z-order는 필터에 나타나는 열에만 해당됩니다; 재클러스터링은 데이터를 다시 쓰고 컴퓨팅 비용이 발생합니다.
- 전후에 파일 수와 평균 파일 크기를 확인하세요; 둘 다 변경하지 않은 유지보수 작업은 아무것도 하지 않은 것입니다.
- 엔진이 제공하는 경우 드라이 런 진공 또는 고아 제거를 먼저 수행하세요.
6. MERGE, 스키마 진화, 타임 트래블
- MERGE: 먼저 일치 키에 대해 소스를 중복 제거하세요(중복 소스 일치가 실패하거나 이중 적용됨). ON 절에 파티션 또는 클러스터링 조건자를 포함하여 엔진이 테이블을 스캔하는 대신 파일을 제거하게 하세요.
- 대규모 테이블에서 반복적인 전체 테이블 MERGE보다는 추가 + 주기적 MERGE 또는 멱등 키를 선호하세요.
- 스키마 진화: 추가 열은 안전합니다; 이름 변경 및 유형 확장은 포맷과 버전에 따라 다릅니다(Delta의 열 매핑, Iceberg의 필드 ID). 이름을 변경하기 전에 관련 리더의 지원을 확인하세요. 제어할 수 없는 프로덕션 파이프라인 입력에 대해 자동 스키마 병합을 활성화하지 마세요.
- 타임 트래블은 디버깅, 감사 및 롤백 도구이지 백업이 아닙니다. 보존된 파일 비용이 발생하며, 보존 기간이 길수록 더 많은 스토리지와 느린 메타데이터가 필요합니다. 보존 기간을 의도적으로 설정하고 중요한 테이블의 실제 백업을 유지하세요.
7. PySpark 4.x 필수 사항
- 변환은 지연됩니다; 액션 전까지는 아무것도 실행되지 않습니다. 하나의 계획을 세우고 하나의 쓰기를 트리거하세요; 반복된
count()또는show()는 명시적으로 캐시하지 않으면 전체 계획을 다시 실행합니다. - 큰 데이터에 대해
collect(),toPandas()및.rdd를 피하세요; 모든 데이터를 드라이버로 가져옵니다. 먼저 제한 또는 집계하세요. - Python UDF 대신 내장 함수 또는 SQL 표현식을 사용하세요. UDF는 최적화 프로그램 푸시다운을 차단하고 행을 직렬화합니다. 불가피한 경우 벡터화된(Arrow) UDF를 사용하세요.
- 넓은 작업(join, groupBy, window, distinct)은 셔플을 발생시킵니다. 이전에 필터 및 프로젝트를 수행하고
explain()으로 계획을 확인하세요. - 조인의 작은 쪽을 브로드캐스트하세요(
broadcast(df)); 실행자 메모리에 맞는 경우에만; 제한 없는 테이블을 브로드캐스트하지 마세요. - Adaptive Query Execution은 Spark 4.x에서 기본적으로 활성화되며 파티션 병합, 스큐 조인 및 조인 전략 전환을 처리합니다; 계획을 읽은 후에 조정하고, 그 전에 조정하지 마세요.
- 스큐는 하나의 느린 작업으로 나타납니다; AQE가 불충분함을 증명한 후에만 핫 키를 소금치거나 격리하세요.
- Spark Connect는 클라이언트-서버 모드입니다: 코드는 원격 세션을 통해 씬 클라이언트로 실행됩니다. Connect 코드에서 드라이버 로컬 JVM 내부(
sparkContext, RDD API)에 의존하지 마세요. - 추측된 숫자로 리파티셔닝하는 대신 압축을 통해 명시적 대상 파일 크기로 쓰세요.
8. 로컬 우선 경로
클러스터 비용을 지불하기 전에 DuckDB 또는 polars로 개발 및 테스트하세요.
- DuckDB는 확장을 통해 Delta 및 Iceberg를 읽고 DuckLake 카탈로그를 연결합니다; polars는 지연 평가와 조건자 푸시다운으로 Delta 및 Iceberg를 읽습니다.
- 실제 테이블 레이아웃에 대해 샘플 또는 하나의 파티션을 사용하여 파일 제거 동작이 일치하도록 하세요.
- 실제 엔진에서 쓰기 의미(MERGE, 삭제 벡터, v3 기능)를 확인하세요; 로컬 리더는 포맷 기능보다 뒤처질 수 있습니다. 성공적인 로컬 읽기는 필요 조건이지 충분 조건이 아닙니다.
절차
- 숫자 확보: 크기, 성장, 작성자, 리더, 엔진. 2. 섹션 1 진행; 가장 저렴한 적합에서 중단. 3. 레이크하우스인 경우: 카탈로그 선택, 그 다음 포맷(섹션 4, 3). 4. 첫 로드 전에 레이아웃 및 유지보수 일정 설정. 5. 로컬에서 프로토타입(섹션 8), 그 다음
explain()출력을 검토한 클러스터에서 실행. 6. 가정이 아닌 파일 수와 쿼리 계획으로 확인.
알려진 문제점 및 해결 방법: references/gotchas.md 참조.