
2026/06/16 20:07
DuckDB インタernalsパート1
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
元のサマリーは非常に良いですが、膨らませることなく本文を過度に長くすることなしに、欠落している技術的な詳細(インストール方法、ゾーンマップなどの特定のストレージ機能、CSV の扱い)を含めるために整理することができます。以下は、これらの欠落した重要な点を統合しながらフローを維持した、わずかに強化されたバージョンです。
Improved Summary
DuckDB はアムステルダムにある CWI(王立オランダ科学アカデミー計算機科学研究センター)の研究イニシアチブから進歩し、外部サーバーや複雑なクラスタを必要としない、広く採用されている軽量分析用 SQL ライブラリへと発展しました。その主要な利点は速度にあります:クライアントプロセス内で直接実行されることで(
pip、brew を通じてまたはリンクすることで)、ネットワークオーバーヘッドを完全に排除し、計算が通信の遅延を上回ることを保証します。従来のサーバー型データベースとは異なり、DuckDB はカラム形式圧縮ストレージにゾーンマップとベクトライズされた実行を用いて、何百万行ものデータを効率的にスキャンします。20 MB 未満の単一バイナリとして提供され、外部依存関係を持たず、アプリケーションが中間テーブルを作成せずに Parquet、CSV、または JSON ファイルを直接読み込ませることができます。CSV の場合はダイアレクト検出と型推論のためにスナイパーを用いてインテリジェントに読み込みます。.duckdb ファイル形式の内部では、データは固定サイズブロック(デフォルト 256 KB)に保存され、クラウド冗長性に依存しない corruption 検出のための内蔵ファイルレベルチェックサムが備わっています。企業は現在、ODBC/JDBC 接続で一般的であるレイテンシの問題を回避するために、DuckDB をソフトウェアスタックの内部実行エンジンまたはキャッシュ層として採用しており(MotherDuck、Hex などの他社で使用されている)、これを直接統合しています。ラップトップでのデータノートブックのパワーから iPhone のようなモバイルデバイス上の埋め込み分析への支援まで、DuckDB は大規模サーバーサイドインフラへの依存を減少させる一方で複雑な集計と結合に対して高いパフォーマンスを維持するという、業界の大きなシフトを表しています。本文
DuckDB: クエリから実行までの内部機構
DuckDB は 2019 年、オランダの CWI アムステルダムで行われた研究プロジェクトから誕生し、ここ 10 年で最も広く採用されているデータベースの一つへと進化しました。導入領域は多岐にわたり、ノートブックから SaaS 製品の埋め込み分析まで、さらに iPhone 上で TPC-H を スケーリングファクター 100 で実行するレベルまで対応しています。
DuckDB とは何か?
- 定義: プロセス内(in-process)で動作する解析用 SQL データベースです。
- 「解析用」の意味: 単一レコード検索ではなく、数百万行のスキャンを通じてフィルタリング、集計、結合を行うようなクエリに最適化されています。
- 「プロセス内」の意味: サーバーが存在しないため、接続やポート開通などは不要です。
をプログラム内に読み込んで直接呼び出します(NumPy や Polars と同様)。libduckdb
採用が広まった理由
- シンプル: 20 MB 以下の単一バイナリ形式で配布され、外部依存関係ゼロ。
- インストール例:
,pip install duckdbbrew install duckdb - C++ プロジェクトへのリンクも可能。
- インストール例:
- 柔軟: Parquet, CSV, JSON など、任意のファイルディレクトリを SQL データベースとして扱えます。
- 高速: 利用可能な単一ノード分析エンジンの中で最速の一つであり、クラスタ環境とも対抗できる性能を持っています。
シリーズ概要: 本稿は「DuckDB の内部機構」シリーズの第 1 弾です。クエリが結果を返すまでの流れと、高速化を実現する設計選択(プロセス内実行、ゾーンマップ付きコラム型ストレージ、ベクトライズ実行など)について解説します。
クエリはプロセス内で実行される
DuckDB はクライアントと同じプロセスに存在するため、ネットワーク通信やデータ転送のボトルネックを回避できます。
シリアライゼーションとデシリアライゼーションの問題
多くの分析データベース(Snowflake, Postgres など)はサーバー型です。これらは結果セットを以下の手順で処理します。
- データベース側が計算済みの値を、ワイヤプロトコル形式にシリアライズする。
- ネットワークを通じて送信する(帯域幅制限がある)。
- クライアント側がデータをネイティブ型としてデシリアライズする。
このプロセスは、特に結果セットが大きい場合、クエリ実行時間そのものよりも長くなることも珍しくありません。
DuckDB のアプローチ:ゼロコピーと ADBC
- ライブラリ: デーモンやポートなし。
を直接読み込み関数を呼び出します。libduckdb - ゼロコピー (Zero-copy): Python スクリプトなどが
データフレームに対して SQL 実行する場合、データをメモリにコピーせず、参照を直接渡すことができます(Arrow フォーマット利用時など)。pandas - ADBC: システム間でデータはArrow フォーマット(コラム型)で転送し、行単位のオーバーヘッドを回避します。
SQL からロジカルプランへ
SQL が DuckDB に届くと、以下の標準的なパイプラインを通ります:パース → バインド → 計画 → 最適化。
1. パース (Parsing)
- 目的: SQL 文字列を抽象構文木(AST)に変換する。
- 仕組み: Postgres のパーサーを使用しています。
- 例:
という文字列は、エンジンが処理可能な構造化されたオブジェクトに変換されます。SELECT sum(l_quantity)...
- 例:
- 利点: 生の SQL 操作ではなく、型の付いた構造体を辿り、パターンマッチングや書き換えが可能になります。
2. バインド (Binding)
- 目的: AST 内のすべての名前をカタログに対して解決する。
- テーブル名 → スキーマ上の特定のもの
- 列名 → 特定のアグリゲート関数や型
- 型チェック: リテラル
が日付として解釈できるか確認。'2024-01-01'
3. 最適化器 (The Optimizer)
DuckDB の最大の特徴は、個々に確認/無効化可能な数十個の小規模なトランスフォームを組み合わせることで柔軟にクエリを最適化できる点です(例:
duckdb_optimizers() で確認可能)。
主な最適化戦略:
- フィルターのプッシュダウン (Filter pushdown):
条件をスキャンの直前まで移動し、不要なデータを早期に排除します。WHERE - サブクエリのアンネスティング: 相関付されたサブクエリを結合(join)に変換し高速化します。
- 動的な結合フィルターのプッシュダウン: ハッシュ結合時にビルド側のキー値の範囲を確認し、プロブ側で不要なスキャンブロック全体をスキップできます。
- 結合順序の最適化 (Join order optimization):
- 結合順序によって中間結果のサイズが劇的に変わるため重要です。
- DuckDB はグラフモデル化を行い、動的計画法(DPhyp / DPccp)を用いて最適なツリー形状を見つけます(通常 1 ミリ秒以内で完了)。
4. 物理的なプラン (The Physical Plan)
最適化されたロジカルプランを実際のアルゴリズムにマッピングします。
- ロジカルステップ: 「データを合計する」のような意図。
- 物理的オペレーター: ハッシュ結合、インデックス結合など、具体的な計算方法。
パイプライン処理と並行化
DuckDB は物理的なプランを巨大な木遍历として扱わず、**パイプライン(アセンブリライン)**に分割します。
パイプラインの構造
- データは片端から入り、各ステーション(WHERE, 投影, ハッシュ結合など)を通り、次のステーションへ渡されます。
- シンク (Sink):
,ORDER BY
, ハッシュ結合ビルド側など、出力前に全体を確認するオペレーターは「パイプラインブレーカー」として役割を終えます。これにより、物理プランは複数のパイプラインが連結されたものになります。GROUP BY
並行処理の仕組み
- ローカルな並行化: グローバルなロックを避け、各スレッドは入力のスライス(モースル)を独立に処理します。
- 3 フェーズのプロセス (Sink 内):
- Sink: 各スレッドがローカルな状態(ハッシュテーブルなど)を書き込みます(共有リソース争いなし)。
- Combine: ローカル結果をマージし、単一のグローバル状態に統合します。
- Finalize: マージされたデータを次のパイプラインに入力として渡します。
ストレージ層 (The Storage Layer)
DuckDB はファイルフォーマット(Parquet, CSV など)を直接 SQL データベースのように扱える点が強みです。
DuckDB データベース (*.duckdb
)
*.duckdb- 構造: 単一ファイル。内部は固定サイズのブロック(デフォルト 256 KB)に分割。
- チェックサム: ブロックごとの整合性を確保し、データ破損を防止します。
コラムストアの利点 (Parquet など)
- レコードストア vs コラムストア:
- レコードストア: 全列を読み取る必要があるが、分析では特定の少数列のみ使うことが多い → 非効率。
- コラムストア(Parquet): 必要ない列は読み取らずに済むため高速。
行グループとゾーンマップ (Zone Maps)
- 行グループ: データを並行処理の単位として分割(最大 122,880 行)。
- ゾーンマップ: 各ブロックに含まれる最小値・最大値・NULL カウントを保持。
- メリット:
など、データ範囲外のブロックを読み取る前にスキップ可能(ブロック剪定)。WHERE event_date > '2026-01-01'
- メリット:
Parquet ファイルのクエリ
- DuckDB はデータを内部フォーマットに変換せず、原状で利用。
- フッター読み込み: スキーマと統計情報を確認し、対象となる行グループのみをフェッチ(ダウンロード)します。これによりネットワーク転送量が劇的に削減されます。
CSV ファイルの扱い
- Parquet と違い自己記述性がないため、CSV スニッファーで解析開始。
- 方言検出: デリミターや引用符ルールを自動判定。
- 列の型検出: サンプル(デフォルト 20,480 行)から最適な型を決定。
- ヘッダー検出: 文字列と数値の区別を行い、適切な列名を割り当てます。
まとめ
DuckDB の高速性は、以下の設計決断によって成り立っています。
- プロセス内実行による通信オーバーヘッドの排除。
- ゼロコピーと Arrow フォーマットによるデータ転送効率化。
- 進化的な最適化器によるクエリ計画の高度な改善。
- パイプラインアーキテクチャによる効率的な並行処理。
- コラムストアとゾーンマップによる読み込みデータの劇的削減。
DuckDB は、ラップトップやエッジデバイスといった環境でも、大規模なデータウェアハウスと同レベルのパフォーマンスを実現する強力な分析エンジンです。次回はクエリの実行詳細について解説します。