DuckDB 내부 구조 개요
DuckDB 내부 구조 개요 (Overview of DuckDB Internals)
DuckDB 엔진이 쿼리를 처리하는 내부 과정을 간단히 살펴보는 페이지예요. 파서(Parser), 바인더(Binder), 논리 플래너, 옵티마이저, 실행 단계가 어떻게 연결되는지 함께 알아볼까요?
출처: 문서
본문
팁 DuckDB 내부 구조의 상세한 설명은 [Design and Implementation of DuckDB Internals ("DiDi")]({% link _library/2026-03-19-design-and-implementation-of-duckdb-internals.md %}) 라이브러리 항목을 참고하세요.
이 페이지는 DuckDB 엔진의 내부 구조를 간단히 설명해요.
파서 (Parser)
파서는 쿼리 문자열을 다음 토큰으로 변환해요:
파서는 카탈로그나 데이터베이스의 다른 어떤 측면도 알지 못해요. 테이블이 존재하지 않아도 에러를 던지지 않고, 어떤 컬럼 타입도 아직 해석하지 않아요. 단지 쿼리 문자열을 지정된 토큰 집합으로 변환할 뿐이에요.
ParsedExpression
ParsedExpression은 SQL 문 안의 표현식을 나타내요. 예를 들어 컬럼 참조, 덧셈 연산자, 상수 값일 수 있어요. ParsedExpression의 타입은 그것이 무엇을 나타내는지 가리키는데, 예를 들어 비교는 ComparisonExpression으로 표현돼요.
ParsedExpression은 CAST 문장 같은 명시적 타입을 가진 노드를 제외하고는 타입을 갖지 않아요. 표현식의 타입은 Parser가 아니라 Binder에서 해석돼요.
TableRef
TableRef는 어떤 테이블 소스든 나타내요. 기본 테이블에 대한 참조일 수도 있지만, 조인, 테이블 생성 함수 또는 서브쿼리일 수도 있어요.
QueryNode
QueryNode는 (1) SELECT 문, 또는 (2) 집합 연산(즉 UNION, INTERSECT, DIFFERENCE) 중 하나를 나타내요.
SQL Statement
SQLStatement는 완전한 SQL 문을 나타내요. SQL Statement의 타입은 그것이 어떤 종류의 문인지 나타내요 (예: StatementType::SELECT는 SELECT 문을 나타내요). 원래 쿼리 문자열에 여러 쿼리가 있으면 단일 SQL 문자열이 여러 SQL 문으로 변환될 수 있어요.
바인더 (Binder)
바인더는 모든 노드를 그들의 bound 등가물로 변환해요. 바인더 단계에서:
- 테이블과 컬럼이 카탈로그를 사용해 해석돼요
- 타입이 해석돼요
- 집계/윈도우 함수가 추출돼요
다음 변환들이 일어나요:
- SQLStatement →
BoundStatement - QueryNode →
BoundQueryNode - TableRef →
BoundTableRef - ParsedExpression →
Expression
논리 플래너 (Logical Planner)
논리 플래너는 bound 문장에서 LogicalOperator 노드를 만들어요. 이 단계에서 실제 논리 쿼리 트리가 만들어져요.
옵티마이저 (Optimizer)
논리 플래너가 논리 쿼리 트리를 만든 뒤, 옵티마이저들이 그 쿼리 트리 위에서 실행되어 최적화된 쿼리 플랜을 만ㄷ르어요. 다음 쿼리 옵티마이저들이 실행돼요:
- Expression Rewriter: 표현식을 단순화하고 상수 폴딩(constant folding)을 수행해요
- Filter Pushdown: 필터를 쿼리 플랜 아래로 밀어넣고 동등성 집합 위에서 필터를 복제해요. 또한 (정적으로 false로 평가되는 필터 때문에) 비어 있다고 보장되는 서브트리를 가지치기해요.
- Join Order Optimizer: 동적 프로그래밍으로 조인을 재정렬해요. 구체적으로 Dynamic Programming Strikes Back 논문의
DPhyp알고리즘을 사용해요. - Common Sub Expressions: 불필요한 중복 실행을 막기 위해 projection과 filter 노드에서 공통 서브표현식을 추출해요.
- In Clause Rewriter: 큰 정적 IN 절을 MARK join이나 INNER join으로 다시 써요.
컬럼 바인딩 해석기 (Column Binding Resolver)
컬럼 바인딩 해석기는 특정 테이블의 컬럼을 참조하는 논리 BoundColumnRefExpression 노드를, 실행 엔진에서 전달되는 DataChunks의 특정 인덱스를 참조하는 BoundReferenceExpression 노드로 변환해요.
물리 플랜 생성기 (Physical Plan Generator)
물리 플랜 생성기는 결과 논리 연산자 트리를 PhysicalOperator 트리로 변환해요.
실행 (Execution)
실행 단계에서 물리 연산자가 실행되어 쿼리 결과를 만들어요.
DuckDB는 push-based 벡터화 모델을 사용하는데, DataChunks가 연산자 트리 위로 밀려 올라가요.
더 자세한 내용은 Push-Based Execution in DuckDB 강연을 참고하세요.