インフラ・DevOps
嘘、ひどい嘘、そしてデータベースベンチマーク
Lies, Damn Lies and Database Benchmarks (questdb.com)
要約
この記事は、データベースベンチマーク、特に異なる種類のデータベースを比較する際の潜在的な偏りと複雑さについて掘り下げています。ClickBenchを例に挙げ、コールドランとホットランの定義、プロセス再起動の扱い、マネージドサービスとセルフホストシステムの非対称性など、測定方法の微妙な違いが結果をいかに歪めるかを詳細に説明しています。著者は、完全に公正なベンチマークは存在せず、最も信頼できる結果は自身の環境で測定することから得られると結論付けています。
全文翻訳
QuestDBは、取引フロアからミッションコントロールまで、要求の厳しいワークロードに対応するオープンソースの時系列データベースです。超低レイテンシ、高い取り込みスループット、多層ストレージエンジンを提供します。ParquetとSQLのネイティブサポートにより、データはポータブルでAI対応となり、ベンダーロックインはありません。ベンチマーク、誰もがベンチマークを好みます。人々はベンチマーク結果を見て、データベースXはデータベースYよりもはるかに高速なので、最高峰であるという噂を広め始めます。まともなベンチマークは、「より速く、より高く、より強く(Citius, Altius, Fortius)」の原則が正確に実装された厳格なオリンピックのようなランニング競技として描かれるかもしれません。しかし実際には、アスリートに近づくと、予期せぬノイズが聞こえ始めます。それは何でしょうか?実は、その競争はインターネットで見かける奇妙なコンテストに似ています。アスリートは、できるだけ速く走りながら「イエローサブマリン」を正確に口笛で吹かなければなりません。勝者はもはや最速のランナーではありません。それは、走ることとは何の関係もないスキルと生のスピードのバランスを最も良く取れた人であり、トラックで最速のスプリンターが簡単に最下位になることもあります。この例えは、特にかなり異なるカテゴリのデータベースが比較される場合など、データベースベンチマークのように複雑なものに当てはまります。完璧で完全に公正なデータベースベンチマークはユニコーンのようなものです。見つけるのは大変でしょう。今日は、公開され、よく知られているベンチマークを使ってこれを説明しようと思います。使用するベンチマークはClickBenchですが、誤解しないでください。私たちはClickBenchそのものを疑問視しているのではなく、すべてのデータベースベンチマークを疑問視しています。ClickBenchはただ便利です。分析データベースにとって堅実な比較であり、すでに多数のエンジンが含まれています。
ClickBenchがどのように測定するか ClickBenchは、約1億行と105列の単一のウェブ分析テーブル(有名なヒットデータセット)と、それに対する43の分析クエリという同じワークロードをすべてのシステムに対して実行します。各エンジンは少数のシェルスクリプトを提供します。フローは常に同じです。スクリプトがデータベースをインストールし、データをロードし(CSV/TSVからのインポート、または外部ファイルを読み取れる場合はダウンロードしたParquetファイルをエンジンに指定するだけ)、そして43のクエリを実行します。各クエリは2つの方法で測定されます。コールドラン。これはクエリの最初の実行であり、事前にすべてのオペレーティングシステムページキャッシュとデータベースキャッシュがクリアされます。これは、何もウォームアップされていない最悪のケースを捉えます。ホットラン。ClickBenchのルールを引用すると、「43のクエリそれぞれが3回実行され」、「2回目と3回目の実行が両方成功した場合、小さい方のランタイムが使用されます」。最初の実行はキャッシュを事前投入することを目的としているため、後の2回の実行が最も速くなることが期待されます。このコールドの定義には、公開ダッシュボードが宣伝しない非対称性が隠されています。OSページキャッシュをクリアし、サーバーを再起動することは、データベースがベンチマークマシン上で実行されている場合にのみ可能です。例えば、Snowflake、BigQuery、Redshift、Databricksなどのマネージドクラウドサービスは、プロバイダーのハードウェア上で実行され、ハーネスにはシェルもdrop_cachesもなく、サーバーをバウンスする方法もありません。そのため、その3回の実行はすべて、再起動されることのない同じライブサービスにヒットします。したがって、そのコールドの数値は、セルフホストエンジンのように強制的にコールドにされることは決してなく、コールドランのランキングはホスト型システムに有利に傾き、それによってコールドランを組み込んだ総合スコアも偏ります。ClickBenchのルールでは、真のコールドランには再起動が必要であり、再起動は制御しているサーバーにのみ要求できるものです。この記事のすべてのエンジンは同じボックスでセルフホストされているため、すべて同じルールで実行されますが、次回ホスト型システムとセルフマネージドシステムのコールドランの数値を比較する際には、この点を覚えておく価値があります。私たちはホットランの結果のみに焦点を当てます。個々のクエリも比較せず、総合スコアのみを比較します。スコアは公開ダッシュボードに表示されるものです。各クエリについて、ClickBenchはそのクエリで最速のシステムに対する比率を計算します。比率 = (0.01 + hot_time) / (0.01 + baseline_time) ここでbaseline_timeは、比較対象システムの中でそのクエリの最高のホットタイムです。0.01は10msのクッションで、10ms未満のクエリが支配的になるのを防ぎます。最終スコアは、これら43のクエリすべての比率の幾何平均です。数値が低いほど良く、仮に1.000であれば「すべてのクエリで最速」を意味します。失敗したクエリは厳しくペナルティを受けます。以下のすべての完全な結果は、最後にリンクされているサポートリポジトリにありますので、ご自身で再スコアリングできます。
INFO
ここがこの記事全体を動かす微妙な点です。すべてのClickBenchクエリスクリプトは、エンジンの内部クエリ時間(DuckDBのRun Time、ClickHouseの--time、DataFusionのElapsed、QuestDBのtimings.execute、Polarsのinternal elapsed)を記録します。プロセスおよびクライアントの起動は、誰の記録された数値にも含まれていません。したがって、プロセスを生かし続けることは、起動項目を削除することでスコアを変更することはできません。なぜなら、その項目はそもそも数値に含まれていなかったからです。それはプロセスローカルのキャッシュのウォームアップを通じてのみスコアを変更できます。これを覚えておいてください、後で重要になります。
テストスタンド ClickBenchの参照実行はAWSのc6a.4xlarge VMを使用します。私たちのものはそうではありません。この記事のすべての数値は単一のボックスで測定されました。AMD Ryzen 9 7900(12コア/24スレッド、最大5.49 GHz、64 MiB L3)、61 GiB RAM、NVMe SSD、Ubuntu 24.04、適切な冷却によりかなり安定したパフォーマンスです。ハードウェアが異なるため、私たちの絶対値を公開ClickBenchダッシュボードと比較しないでください。ここで重要なのは、すべてのエンジンがまったく同じマシンを見る、私たちのボックス内での再ランキングだけです。そして最も信頼できる数値は常に、ご自身のマシンで測定したものです。
競合 バージョンが正確でなければベンチマークは再現可能ではなく、公正に評価することもできません。これらは急速に変化します。以下に正確な実行内容を示します。
Engine Version Notes
DuckDB 1.5.4 upstreamと一致(最新版をインストール)
ClickHouse master 26.6.1.909 upstreamと一致(インストーラーがmasterをプル)
DataFusion 53.1.0 upstreamピン、ソースからビルド
Salesforce Hypertableau hyperapi 0.0.25080 PyPIの最新版、両側
Polars latest on PyPI at run time upstreamピンなし
CrateDB 6.3.3 upstreamは5.10.10をピン留め。私たちは最新の安定版に上げた
QuestDB 9.4.3 upstreamは9.3.1をピン留め。私たちは上げた、以下参照
upstreamのピンからの2つの意図的な逸脱があります。CrateDBは、upstreamのaptインストールがとにかく最新の安定版(6.3.3)をプルするためです。QuestDBは、バージョン9.3.1がClickBench PR #902で問題を起こすためです。このPRは2つの実際のバグを文書化しています。9.3.1にはlength_bytes()関数が不足しており(9.3.2で追加)、クエリ27と28が完了しません。また、query.timeout.secからquery.timeoutへの設定名変更よりも前のバージョンであるため、タイムアウトの引き上げが黙って何も機能しません。私たちは実行時に最新版である9.4.3に移行し、両方の問題を修正しました。ピン留めされていない2つのエンジン、PolarsとHyperの正確な解決バージョンは、結果の隣にあるサポートリポジトリに記録されており、全体の実行は再現可能です。
シナリオ1:Parquetタイリング競争 簡単なことから始めましょう。データセット(約14GB)を保持する単一のParquetファイルがあり、それをクエリする必要があります。外部Parquetを直接読み取れる5つのエンジン、DuckDB、Polars、ClickHouse、DataFusion、Salesforce Hyperを選択しました。そのうちの4つは、クエリが呼び出されるたびに新しいCLIまたはPythonプロセスを立ち上げます。Polarsは例外で、ClickBenchはそれを長期間実行されるPythonセッションとして駆動します。これは後で重要になります。以下は、私たちのボックスでのバニラClickBenchの結果です。
Vanilla ClickBench、単一のParquetファイル。数値が低いほど良い。
rank system hot score
1 DuckDB 1.353
2 Polars 1.361
3 DataFusion 1.955
4 ClickHouse 2.082
5 Hyper 3.609
5つすべて強力なエンジンですが、ここでは一部が明らかに高速です。DuckDBとPolarsがリードし、ほぼ同点です。DataFusionとClickHouseは中間で、Hyperはかなり後れを取っています。これで決着でしょうか?
部屋の中のゾウ CLIまたはPythonプロセスをすべてのクエリ実行ごとに再起動することは本当に公平でしょうか?これは未解決の問題であり、「Salesforce Hyper: Hot runs are measuring cold times」というタイトルのClickBench issue #936の主題です。この問題が示すように、クエリスクリプトは「実際にサーバーを再起動している」のです。