DuckDBを使ってDelta Lakeのデータを読み取る
巷で話題のDuckDBですが、私の扱っているデータは主にDelta Lakeで保存されています。
DuckDB=databaseという話も聞きますので、併用は難しいか〜と思っていたらこんな記事を見かけました。
え? duckdbってdatabaseじゃないの? まあ、最近普通にRDBもAthenaやazure synapeseもcsvとかpathで読みにいけるのも多いし、そういうこと?・・・
ということでこの辺り探っていきたいと思います。また、duckdbは初心者です。よろしくお願いします。
DuckDBとは
DuckDB is a fast
open-source/portable/analytical
database system
と思ったのですが、特徴を説明する欄には以下の説明がありました。
Feature-rich
DuckDB offers a rich SQL dialect. It can read and write file formats such as CSV, Parquet, and JSON, to and from the local file system and remote endpoints such as S3 buckets.
つまりcsvやparquetなど自由に扱えますよということです。ファイルの実体はduckdb自体が管理するのではなく、ファイル形式で保存される様です。
また以下の様な記述もあります。
For DuckDB, there is no DBMS server software to install, update and maintain.
つまりはストレージを一体として持つ様な従来のRDBMSとは異なる様です。
私は Database systemと聞くと、NOSQLとか key-valueとかそういう細かいことはさておき、OracleとかMySQLとかSQLServerとかストレージと処理エンジンが一体になったシステムを想像してしまいます。
DuckDBはそうではなく、どちらかといえば最近のモダンなコンピュート層とストレージが層が別れた、どちらかといえば単体の処理エンジンに近い感覚を持ちました。
また実装の容易さはpandasやpolarsに近いとすら感じます。
追記:こういうの組込みデータベースというそうです。勉強になりました。↓
Native Delta Lake Support in DuckDBとは
まずこのプロジェクトはDatabricksと一緒になって取り組みが始まった様です。
構成要素として以下の3つが登場します。
Delta Lake, Delta Kernel, DuckDB Delta extension.
Delta Kernelが個人的に初耳ですが、順に理解を進めていきます。
Delta Lakeとは
Delta Lakeとはの理解はこの後の話を進める上で軽く説明が必要だと感じたので、特徴をいくつか記載しておきます。(DuckDBは初なので、温度差をご了承ください)
- icerbarg等と同等のopen source lakehouse formatsです。
- lakehouse formats = acid transaction等を兼ね備えているのに非構造なデータも(もちろん構造化データも)取り扱えるデータレイク層を提供します。
-
The Delta Kernel
上記で説明したDelta Lakeとはは特別なプロトコルであるため、取り扱う際にはそこそこ実装コストがかかります。簡単ではありません。
代表的なlibraryとしてはdelta-rsが存在し、こちらを使うことが私も一般的に多いと思います。 DeltaTable(tmp_delta_path).to_pandas()
とかよくみるコードです。
しかし、delta-rsは DuckDBを意識して作られたものでは無いため、delta-kernel-rsという新しい規格を作って DuckDBと効率的に繋げよう!というこの仕組みがDelta Kernelです。
(https://duckdb.org/2024/06/10/delta.html#the-delta-kernel より)
DuckDB Delta Extension delta_scan
delta_scan
これによってdelta tableを DuckDB経由で読み取ることができます。
Delta kernelがコアなプロトコル管理の話だとすると、これはAPIの側の話になります。
また既存の read_parquet
とコピーの方法は同じであるが、大きく異なる点はdeltatable特有のmetadataを参照して読み込みを行うことです。
metadataの中には過去の削除した履歴等残っているので、これはdelta tableを正しく読み込む上でmustです。これらを全て統合したのが今回のdelta_scanという訳ですね。
(https://duckdb.org/2024/06/10/delta.html#the-delta-kernel より)
ベンチマーク
それでは最後に以下の三つでどのくらいクエリが早いのか調べてみたいと思います。
- duckdb
- polars
- pandas
- Spark
データはかなり小さい(数MB)のを group by してみようと思います。
個人的には上に書いた順通りに早くなってほしいなと思っています。
また、上記の条件だと多分Sparkは最初から圧倒的に不利ですし、pandasとかは一度Deltalakeからpandasへの変換を挟んでいる時点で不利ですが、そういうのを全部ひっくるめた上で、「Data Analystが Gold tableまで集約された某データを集計するのに最速なものはなんだ選手権」ということでいきたいと思います。
Platformは、もともとDatabricksを強く意識して書き出したブログなので、Databricksを利用しました。また、データもDatabricks-datasetsのcustomerを使用します。
結果
1位:duckdb: 1.67 ms ± 30.8 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
duckdb.sql(
f"""
SELECT sum(c_acctbal)
FROM delta_scan('{tmp_delta_path}')
group by c_nationkey
;
"""
)
2位:polars: 208 ms ± 2.75 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
df = pl.read_delta(tmp_delta_path)
df.group_by("c_nationkey").agg(pl.col("c_acctbal").sum())
3位:spark: 946 ms ± 190 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
(まあsparkは大規模データ処理とスケーラビリティが売りですから)
(後から気づきましたがcache効いちゃってるので、そのうち検証し直し、cashe clearしたら勘ですがダントツビリになると思います。)
4位:pandas: 2.18 s ± 44.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
お前にもread_deltaがあれば話が違っただろうか・・・
DuckDB半端ねえな
テストしたタスクN=1とはいえ、思ってたよりずっと速度差が出ました。
ここまで早いと今後利用を検討しないといけないですね。これからはDuckDB勉強していこうと思います。(まあSQL何で使うか使わないかですね)
git
最後にコードを共有しておきます。Databricks community Editionでも動きます
Discussion