プログラミング
DuckDB v2.0のプレビュー
A Preview of DuckDB v2.0 (duckdb.org)
要約
DuckDB v2.0(コードネーム「Cyanoptera」)がこの秋にリリース予定です。本バージョンでは、DuckDBをサーバーとして利用する機能、トリガー、新しいVARIANT型、非同期I/O、新しいSQLパーサー、新しいストレージフォーマットなど、多数の重要な新機能が導入されます。特に、クライアント/サーバーモードのサポートと、JSONを高速に扱えるVARIANT型の強化が注目されます。
全文翻訳
DuckDB v2.0のプレビュー
Mark RaasveldtとHannes Mühleisen
2026-08-17 | 15分
TL;DR: DuckDB v2.0がこの秋にリリースされます。この記事では、その主要な機能である、サーバーとしてのDuckDB、トリガー、VARIANT型、非同期I/O、新しいSQLパーサー、新しいストレージフォーマットなどをプレビューします。
DuckDB v2.0は、西米に生息する鮮やかな赤褐色のカモであるシナモン teal(Anas cyanoptera)にちなんで「Cyanoptera」と名付けられます。メジャーバージョンアップは軽々しく行うものではなく、単なる儀式ではありません。v2.0では、新しいSQLパーサー、新しいデフォルトストレージフォーマット、改訂されたC API、そして慎重に選ばれた少数の破壊的変更が搭載されます。しかし何よりも、これは機能リリースであり、3月にv1.5をリリースして以来、10,000件以上のコミットによって構築されています。昨年の「レイクハウスの年」に続き、このリリースは「サーバーとしてのDuckDBの年」の幕開けとなります。これらの機能の多くは、DuckCon #7の「State of the Duck」トークでプレビューしましたが、読むよりも見る方がお好みの場合はそちらをご覧ください。
DuckDBは非常に速く進化しており、ここではその変更点のほんの一部しかカバーできません。新機能をすべて短縮リストにまとめるのは常に何を含めるかの戦いであり、はい、これがリスト形式(DuckDB v2.0に搭載される10のこと、8番目はあなたを驚かせるでしょう)であることは承知しています。この形式には誇りを持っていませんが、機能するので、ここに掲載します。SQLレベルの機能から始めて、エンジンへと進んでいきます。
1. サーバーとしてのDuckDB: QuackとCONNECT
DuckDBは初日からインプロセスデータベースでした。しかし、人々は私たちに、非常にしつこく、クライアント/サーバーモードを求めてきました。そして私たちはついに折れました。quack拡張機能は、他のDuckDBと通信するためのDuckDBネイティブプロトコルを実装しています。これはDuckCon #7の直前にプレビュー版としてリリースされ、v2.0で安定版に昇格し、DuckDBの方向性の大きな部分を占めています。任意のDuckDBプロセスがネットワーク経由でデータベースを提供でき、他の任意のDuckDBはそれをアタッチして、新しいCONNECTステートメントを使用してクエリをルーティングできます。例:
DuckDBサーバー
CALL quack_serve( token = 'my_token' );
quack: DuckDBクライアント
ATTACH 'quack:server.example.com' AS qk (TOKEN 'my_token');
CONNECT qk;
SELECT count(*) FROM events; -- サーバー上で実行され、結果がストリーミングバックされます
DISCONNECT;
CONNECTは、Quackが最初に公開されたときのremote.query($$...$$)の回避策の後継です。私たちはその構文を見て、「いや、これはダメだ」と思いました。そしてCONNECTはQuackに限定されません。それは、サポートする任意のリモートデータベースにセッションをポイントし、新しいリモートプッシュダウンオプティマイザー(#22914)は、テーブルをワイヤー経由でプルするのではなく、SQLを直接PostgreSQLとMySQLに送信します。
CONNECT 'postgres://localhost/mydb';
SELECT count(*) FROM orders; -- PostgreSQLサーバー上で実行されます
DISCONNECT;
過去に分析システムを扱ったことがあるなら、DuckDBはトランザクションワークロードを処理できないと Assume するかもしれません。しかし、DuckDBは初日から、フルMVCCとトランザクション分離を備えたトランザクション、マルチコネクションデータベースとして構築されてきました。ほとんどのユーザーは、シングルユーザーシナリオでそれを必要としませんでした。DuckDBはトランザクションをうまく処理することが判明しました。多くのワークロードでPostgreSQLのような汎用データベースと競合できるほど高速であり、クライアント/サーバーパターンは、マルチテナント、長期実行デプロイメントでその仕組みを輝かせることができます。DuckDBを長期実行することも新しい課題をもたらすため、v2.0では、DuckDBインスタンスを見て実際に何をしているかを確認できる、より良いメトリクス、ログ、オブザーバビリティ(例:#22799のメトリクスレイヤーのリワークを参照)を推進しています。人々は、プレビューから数週間以内に、Quackプロトコルのスタンドアロンクライアントを構築しました。私たちはDuckDBを他のDuckDBと通信できるように拡張していると思っていましたが、世界は「いやいやいや」と言い、独自のクライアントを構築しました。誰がそれを予想したでしょうか。
2. VARIANTがファーストクラス市民になる
VARIANT型はDuckDB v1.5でリリースされ、JSONの強化版と考えることができます。基本的に、JSONが高速だと想像してみてください。JSONのように、VARIANT列は各行に異なる形状のデータを格納できます。JSONとは異なり、テキスト形式ではありません。DuckDBは、半構造化データに隠された共通の構造を自動的に検出し、「シュレッド」するため、ストレージでの圧縮が良好で、クエリでの実行も高速です。これらはすべて、スキーマを宣言する必要がありません。これにより、VARIANTはリアルタイムログ取り込みに自然に適合します。JSONのようなレコードのストリームは構造を共有しますが、時間とともに進化します。v2.0では、このパイプラインはエンドツーエンドで機能します。ストレージからのシュレッド実行(#20912)、スキャンへの抽出プッシュダウン(#22478)、Parquet用のシュレッドVARIANTの読み書き、そしてvariant_*関数のファミリーです。
CREATE TABLE events (payload VARIANT);
INSERT INTO events VALUES ('{"user": {"id": 42, "tags": ["a", "b"]}}'::JSON::VARIANT);
SELECT variant_type(payload), variant_keys(payload) FROM events;
SELECT * FROM events WHERE variant_contains(payload, '{"user": {"id": 42}}'::VARIANT);
長期的には、v2.0の直後(ただし、確約はできません)に、通常のJSON型をVARIANTでバックアップする予定です。これにより、既存のJSONワークロードは、クエリを変更することなく、これらのメリットをすべて享受できます。
3. トリガー
トリガーは長年の機能リクエストであり、DuckDB v2.0では完全な形で提供されます。BEFOREおよびAFTERトリガー、FOR EACH ROWおよびFOR EACH STATEMENT、REFERENCING OLD/NEW TABLEを介したトランジションテーブル、イベントごとの複数のトリガー、トリガーテーブルでのRETURNING、DROP TRIGGERです。古典的なユースケースは監査テーブルです。システムで何かが発生し、トリガーが変更内容を記録します。例:
CREATE TABLE target (id INTEGER, val INTEGER);
CREATE TABLE audit (id INTEGER, old_val INTEGER, new_val INTEGER);
CREATE TRIGGER trg_audit AFTER UPDATE ON target REFERENCING OLD TABLE AS o NEW TABLE AS n FOR EACH STATEMENT INSERT INTO audit SELECT n.id, o.val, n.val FROM o JOIN n ON o.id = n.id;
INSERT INTO target VALUES (1, 10), (2, 20);
UPDATE target SET val = val * 10 WHERE id <= 2;
SELECT * FROM audit;
id old_val new_val
1 10 100
2 20 200
トリガーは、長期実行DuckDBサービスに自然に適合し、私たちはそれらを内部的に使用していくつかの今後の機能を構築する予定です。これらはSQLレベルでも完全に公開されているため、独自のクールなものを作成できます。
4. SQL方言の追加
いつものように、DuckDBのSQL方言は成長し続けています。このリリースサイクルのお気に入りのいくつかを紹介します。
NEAREST joins(#24137)により、トップk類似性検索がJOIN句になり、ベクトルおよび埋め込みワークロードに便利です。
SELECT q.user_id, t.product_id FROM users q INNER JOIN products t APPROX NEAREST 2 BY SIMILARITY array_cosine_similarity(q.embedding, t.embedding);
CTE内のDML(#21634、#21997、#24217)により、INSERT、UPDATE、DELETE、COPYをパイプラインステップとして使用できます。
WITH moved AS MATERIALIZED (
DELETE FROM staging RETURNING *
)
INSERT INTO archive SELECT * FROM moved;
ネストされたスキーマ(#23492、#24222)により、スキーマ内にスキーマを作成できます。
CREATE SCHEMA finance;
CREATE SCHEMA finance.reports;
CREATE TABLE finance.reports.q3 (revenue DECIMAL);
新しい変数構文(#21194)により、$xを式が許可される任意の場所で使用でき、getvariable(...)という冗長な記述は不要になります。
SET VARIABLE threshold = 100;
SELECT * FROM orders WHERE amount > $threshold;
JSON操作関数json_set、json_insert、json_replace、json_remove(#23786)により、JSONドキュメントをインプレースで変更できるようになりました。
SELECT json_set('{"a":1}', '$.b', '2');
-- json_set('{"a":1}', '$.b', '2')
-- {"a":1,"b":2}
そして、USING KEY集計(#19481)を備えた再帰CTEは、以下に説明する書き直された再帰CTEエンジンによってバックアップされた、純粋なSQLでの反復アルゴリズムを可能にします。
WITH RECURSIVE tbl(a, b) USING KEY (a, avg(b)) AS (
SELECT 1, 5
UNION
SELECT a, b - 1 FROM tbl WHERE b > 0
) TABLE tbl;
a b
1 2.5
その他にも、SQL標準のFETCH FIRST 2 ROWS ONLY(#23533)、OVERLAY()(#22456)、GROUP BYでのUNNEST(#23644)、マルチマッチ行に対する明確に定義されたMERGE / UPDATE ... FROMセマンティクス(#24058)があります。
5. 非同期I/O
S3のようなオブジェクトストアとの連携は、DuckDBエクスペリエンスの中心です。データはどこかから来る必要があります。