비용 기반 옵티마이저(CBO)와 테이블 통계 — ANALYZE TABLE
Apache Spark의 Iceberg/Delta Lake 카탈로그 연동 및 비용 기반 옵티마이저(CBO) 공식 문서 종합 (웹 리서치, 2026) — Apache Iceberg 공식 문서(spark-configuration, spark-ddl) + Databricks CBO 문서 + japila.pl Spark SQL Internals Databricks CBO 공식 문서 + japila.pl Spark SQL Internals(Cost-Based Optimization)
이 모듈을 다 읽으면
- CBO가 통계 없이는 왜 제대로 동작할 수 없는지 설명할 수 있다
- ANALYZE TABLE로 어떤 통계를 수집하는지, 왜 최신 상태로 유지해야 하는지 판단할 수 있다
spark-learn-24에서 Catalyst 4단계 중 'Physical Planning이 비용 모델로 최적안을 고른다'고 짧게 언급했던 부분을 확장해, 그 비용 모델이 실제로 무엇을 근거로 계산되는지(테이블/컬럼 통계) 정리한다.
CBO는 통계가 없으면 눈감고 고르는 것과 같다
spark.sql.cbo.enabled(기본 true)로 켜지는 CBO는, 특히 여러 테이블을 조인하는 쿼리에서 '어떤 순서로 조인할지', '어느 쪽을 브로드캐스트할지' 같은 결정을 각 테이블/컬럼의 실제 데이터 분포를 근거로 내린다. 문제는 이 통계를 직접 수집해두지 않으면 CBO가 참고할 근거 자체가 없어서, 있으나 마나 한 기본 추정치로 계획을 세우게 된다는 점이다 — CBO를 켜는 것만으로는 부족하고, 통계를 수집·최신화하는 운영까지 함께 갖춰야 실제 효과를 본다.
핵심 포인트
- spark.sql.cbo.enabled(기본 true)는 조인 순서·브로드캐스트 여부 등을 실제 데이터 분포 기반으로 결정하려는 것
- 통계를 수집해두지 않으면 CBO가 참고할 근거가 없어 사실상 무력화된다 — 켜는 것과 통계 운영은 별개
ANALYZE TABLE — 무엇을 수집하고, 왜 갱신해야 하는가
ANALYZE TABLE t COMPUTE STATISTICS는 테이블/파티션 단위 통계(총 크기, 행 수)를 수집한다. 컬럼 단위 통계(FOR COLUMNS 절 지정)까지 함께 수집하면 min/max, null 개수, distinct 값 개수, 평균/최대 컬럼 길이, 그리고(옵션으로) 값 분포를 더 세밀하게 표현하는 히스토그램(spark.sql.statistics.histogram.enabled, 기본 비활성)까지 얻을 수 있다 — CBO의 효과를 제대로 보려면 테이블 통계뿐 아니라 조인/필터 조건에 자주 쓰이는 컬럼의 통계까지 함께 수집해야 한다. 이 통계는 스냅샷이라 테이블에 대량 쓰기가 있고 나면 실제 분포와 어긋나게(stale) 된다 — 그래서 데이터가 크게 바뀐 뒤에는 ANALYZE TABLE을 다시 실행해 최신화하는 운영이 필요하다(자동으로 갱신되지 않는다).
핵심 포인트
- ANALYZE TABLE ... COMPUTE STATISTICS로 테이블/파티션 통계(크기, 행 수)를 수집
- FOR COLUMNS로 컬럼 단위 통계(min/max/null수/distinct수 등) 추가 수집, 히스토그램은 기본 비활성(spark.sql.statistics.histogram.enabled)
- 통계는 스냅샷이라 자동 갱신되지 않는다 — 대량 쓰기 후에는 ANALYZE TABLE을 다시 실행해야 CBO가 최신 분포를 반영한다