kdb+ はループを嫌うことで有名です(no stinking loops)。kdb+ 開発者として、私たちはアーキテクチャの選択に多くの柔軟性を持っていますが、今日私が皆さんに訴えたいのは、システムからもう一つ削除すべきものがあるということです:rdb です。stinking rdbs は要りません!

kdb+ の世界では、データを保存する方法は基本的に 2 つあります:メモリ内とディスク上です。データ自体は同一です。これは現在カラムナーデータの分野でかなり一般的になっている重要な洞察です(特に Apache Arrow を参照)。これにより、ある場所から別の場所へデータを移動する際のシリアライズの必要性がなくなり、メモリマッピングとディスクからの非常に高速な読み取りが可能になります。kdb+ はさまざまなアーキテクチャをサポートしていますが、標準またはデフォルトのモデルは依然として、2 つの主要なタイプのプロセスを一緒に使用することです:データがプロセスの独自のメモリ空間に完全に存在するリアルタイムデータベース(rdb)と、データがディスク上にシリアライズされ、プロセスのメモリ空間にメモリマッピングされる履歴データベース(hdb)です。

私の冒頭は意図的に挑発的で、少し皮肉めいています。私は本当に rdb を一切使用すべきではないとは思っていません。技術としての kdb+ の大きな強みはその柔軟性であり、kdb-x はさらに多くのものを追加することを約束しています。ここで私が述べる主張は、rdb をすべての kdb システムにおけるデフォルトオプションと見なすべきではないということです。それらは常に kdb のコアプロセス型 の一つでしたが、多くの問題では必要ないかもしれないと私は考えています。少なくとも使用しないことを検討すべきです。このアプローチは新しいものではなく、多くのインストールや KX の Insights 製品内の独自コンポーネントで採用されていますが、いつどこで適用可能かについての詳細は必ずしも明確ではありません。

なぜ rdb を持つのか?

より高速なクエリ

メモリ内のデータに対するクエリはもちろんはるかに高速ですよね? 私はかなり標準的なハードウェアとかなり標準的なデータで簡単なベンチマークを作成しました:

  • 32GB のメモリと高速 NVMe SSD ストレージが接続された i7i.xlarge ec2 インスタンス
  • NYSE TAQ 取引データの典型的な(比較的小さい)1 日分。およそ 5,000 万件の取引とディスク上で 10GB
  • メモリ内データ(rdb)ディスク上データ(hdb)キャッシュがコールド時のディスク上データ(hdb cold cache) のクエリパフォーマンスを比較
  • 典型的なワークロードをカバーする少数のクエリを使用

bench1

これらの結果についてさらに詳しく話す前の重要な文脈は、hdb プロセスは実際にはデータを取得するためにディスクまで行く必要がないことが多いということです。ディスク上のファイルをメモリマッピングし、アクセス時に OS はまずページキャッシュをチェックします。特定のファイルがそのキャッシュに存在する場合、実際にはディスクまでデータを取得しに行きません。そのため、上記のすべての hdb の結果 では、クエリはディスクではなくページキャッシュにヒットしています。つまり、キャッシュはウォームです。キャッシュがウォームの場合、hdb は rdb とそれほど変わりません:データはメモリ内にありますが、プロセスの独自のメモリ空間ではなく OS レベルのページキャッシュ内にあります。また、キャッシュが「コールド」時の 3 番目のベンチマークセット も追加したので、OS が実際にディスクまで行く必要がある場合の違いを確認できます。

では、これらのベンチマークから得られる主なポイントは何でしょうか:

  • ここでは核心を隠さず述べましょう:メモリ内 がすべてのベンチマークで最速です
  • クエリが属性を使用でき、結果サイズが小さい場合、メモリ内 データは ディスク に対して大きな差で勝ちます。特に最後のクエリは、データがメモリ内にあり属性を持つ場合、非常に高速になります。これらのクエリはハッシュマップ上のルックアップに相当するため、O(1) で非常に高速です。ディスク上のデータにも属性を追加できますが、ディスク上の属性は kdb+ では追記可能ではないため、維持できません。この点については後で触れます。
  • クエリが属性を使用しない場合、または属性を使用しても結果サイズが大きい場合、rdbhdb のパフォーマンス差は小さくなります。クエリによっては 0〜20% の範囲で推移します。この小さな差は、おそらくページフォルトや TLB プレッシャー、その他の技術的な要因によるもので、あまり心配する必要はありません。OS ページキャッシュ内のメモリ内データは、rdb のヒープ内のデータほど高速ではありませんが、近いです。上記の集計と属性付きの大規模スキャンの比較が最も興味深いかもしれません:これらのクエリはどちらも属性の恩恵を受けますが、属性なしの hdb もそれほど遅れていません。クエリは依然として大量のデータにアクセスする必要があるため、属性の利点ははるかに小さくなるからです。つまり、hdb は実際には OS ページキャッシュという共有空間にメモリを持つ、もう一つのタイプのインメモリデータベースと考えることができます
  • クエリが実際に ディスクまで 行く必要がある場合でも、速度の差はそれほど大きくありません。最悪でも 2 倍程度です。ここで現代の NVMe SSD が大きな違いを生んでいます。スピニングディスクではこの差ははるかに大きくなったでしょう

挿入に追従する

データを読み取るだけでなく、新しいデータを追加できる必要があります。新しい取引が日中に流入し、それらを追加できる必要があります。以下は 2 つのセットアップを比較した別のベンチマークです:

bench2

メモリ内 プロセスはここでより明確な優位性を持っています。ただし、予想されるほど大きくはないかもしれません。ストレージが遅いスピニングディスクだった頃に比べて、はるかに小さいのは確かです。そしてもう一つの重要なポイント:メモリへの挿入はシリアルプロセスです。rdb が新しいデータを挿入している間、同時にクエリを提供することはできません。後者のケースではこれは当てはまりません。ディスク上のスプレイテーブルに挿入すると同時に、プロセスはデータに対するクエリを提供できます。コンピュートとメモリが分離されています。

なぜ rdb を持たないのか?

より高速なクエリ

上では rdb が(わずかに)高速であることを示しましたが、それは 1 つのクエリについてです。2 つや 10 個のクエリがある場合はどうでしょうか。rdb ではクエリをシリアルに実行せざるを得ません。クエリを同時に実行することはできません。しかし、データがディスク上に共有され(OS ページキャッシュを介してメモリ内にもある)場合、並列に実行できます。個々のクエリはわずかに長くなるかもしれませんが、10 個すべてを別々のプロセスで同時に実行できます。hdb で 10% 遅いクエリは、2 回実行する必要がある場合、並列に実行できるため 45% 速くなります。例えば、110ms ずつの 2 つのクエリを並列に実行すると合計 110ms になるのに対し、100ms ずつの 2 つのクエリをシリアルに実行すると 200ms かかります。これはまた、同じシステム上で異なるタイプのユースケース(例:リサーチクエリとレイテンシに敏感な取引クエリ)をサポートしたい場合に大きなアーキテクチャ上の利点となります。それらが互いに干渉することなく。

すべてのメモリがデフォルトで共有される

基本的に無料でシャーディングとスケーリングが可能です。rdb には固定のメモリコストがあり、より多くのプロセスが必要な場合はより多くのメモリが必要で、新しいプロセスは現在のすべてのデータをメモリに読み込む必要があるため起動が遅くなります。データがディスク上と OS ページキャッシュ内に一度だけ保存されている場合、その上に 10 個のプロセスを置くことも、100 個でもいいでしょう。これらのプロセスはほぼ瞬時に起動できます。これにより、メモリとコンピュートが根本的に分離されます

ゲートウェイが不要

必要に応じてゲートウェイプロセスを持つことはもちろん可能ですが、rdb と hdb のデータを結合するために必要ではありません。すべてのデータに 1 つのプロセスでアクセスすることが可能です。ユーザーはライブデータと履歴データの両方にアクセスできる独自の単一プロセスを起動できます。

よりシンプルなアーキテクチャ

ディスク上のデータを「ライブ」で追記する際のリロードとマッピングに関する考慮事項はありますが、全体としてプロセスが少なく、プロセス型も少なくなります。rdb と hdb、ゲートウェイ間の調整問題がないため、システムはよりシンプルになる可能性があります。

では、これについてもう少し詳しく話しましょう…

代替のデフォルトアーキテクチャ

標準の kdb+ アーキテクチャ が現在の形になっているのには理由があります。メモリがはるかに高価で、すべての不揮発性ストレージがスピニングディスクである世界では、データベースを 2 つに分割するのは理にかなっています。ディスク上のスプレイテーブルに対する挿入はメモリよりもはるかに遅く、おそらく数桁の差があり、OS ページキャッシュにヒットしないクエリ(コールドキャッシュ)は劇的に遅くなり、やはりおそらく数桁の差があるでしょう。しかし、私たちが今生きている世界はもうそうではありません。

私は次のような代替のデフォルトアーキテクチャを提案します:

arch

ティッカープラントプロセスは以前と全く同じですが、すべてのデータは「ライター」プロセス(本質的には TorQ wdb プロセス)を介して直接ディスクに送られ、すべてのデータはディスク上のデータをマウントするデータベースプロセス(TorQ hdb)を介して読み取られます。

ここで明確にしておきたいのは、これは決して革新的なアイデアではないということです。今日運用されている kdb+ システムの中には、これと同様のアーキテクチャを持つものがあると確信しています。ここでの私の主張は、これが新しいということではなく、この代替案をより真剣に、より広範に検討すべきだということです。

以下は、ディスク上のデータを「ライブパーティション」と通常のすべての履歴データに分離する方法を示すより明確な図です:

arch1.5

このアプローチにはいくつかの重要な利点があると私は考えています:

  • よりシンプル。 プロセスが少なくなるほど問題も少ない
  • より柔軟でスケーラブル。 コンピュートとメモリが分離されています。より多くのデータベースプロセスが必要な場合は、単純に追加できます。直接的なメモリオーバーヘッドはなく、起動も非常に高速です(従来の rdb とは異なります)。OS ページキャッシュを「無料の」メモリ管理として効果的に使用しており、これはコアで実戦済みの OS コードなので非常に堅牢です。
  • データが一箇所にある。 今日のデータと前日のデータが必要な場合、2 つの異なるプロセスに対して 2 つのクエリを実行して結合などを処理する必要はありません。すべてのデータが一箇所にあります

もちろん、ソフトウェアエンジニアリングではすべてトレードオフであり、ここで私たちが行っているトレードオフは以下の通りです:

  • rdb と比較してクエリパフォーマンスがやや低下する。 上で述べたように、差は思ったほど大きくありませんが、実際の差はあります。
  • ライブで追記されているパーティションでは属性を持つことができない。 これは、特定の「小規模ルックアップ」タイプのクエリではパフォーマンス差が劇的になることを意味します。ただし、これらのクエリがワークフローの重要な部分ではない場合や、上記の利点を維持しながらこのタイプのクエリを高速に保ちたい場合は、必要に応じて専用のキャッシュプロセスを追加できます。つまり、大規模で高価な汎用キャッシュプロセス(つまり rdb)ではなく、必要に応じて専門化されたキャッシュを追加できます

「ライブ追記」データベース(dbs、hdb と rdb と呼ぶ必要はもうありません)には、いくつかの重要な技術的考慮事項もあります:

  • クエリがほとんど/全くディスクまで行かなくて済むように、「ライブパーティション」を OS ページキャッシュにロックしたいと考えています。データが頻繁に使用されると、OS は頻繁にアクセスされるデータをキャッシュに保持しようとするため、これは自然に発生します。ただし、vmtouchtmpfs(RAM ディスク)を使用してこれを直接制御することもできます。したがって、常に上記のホットキャッシュベンチマーク結果を得て、ディスクまで行く必要がないことを保証できます。これは比較的簡単に設定できます。
  • データベースプロセスと、ライブで追記されている symfile(列挙型)およびカラムの間に調整の問題があります。これは見た目ほど悪くはありません。これを解決する最善の方法は、毎回のクエリでの mmap オーバーヘッドを避けるために .Q.MAP を使用し、symfile をリロードして最新のデータを定期的に再マッピングするために \l を使用することだと私は考えています。原則として、これは非常に高速に実行できます(私のテストセットアップでは \l の実行に 0.018ms かかりました)。また、システムで望む可用性と速度のトレードオフに応じて、実行方法についていくつかのオプションがあります:
    • 十分に安価であれば、単にクエリの前に実行できます
    • または、ライターが新しいデータがあるたびにすべての db にリロードをトリガーするようにできます
    • または、タイマーベースにすることもできます
  • symfile は頻繁にリロードするため、小さく保つ必要があります。ただし、これについてスマートにすることもでき、オプティミスティックにリロードできます。つまり、チェックサムを使用して変更があったかどうかを素早く確認し、必要な場合にのみリロードします。
  • 「ライブ」パーティションでは属性を持つことができません。従来の hdb パーティションは最も一般的にフィルタリングされるカラムに p 属性を配置し、ルックアップ用のインデックスとして機能します。kdb+ のディスク上の属性は根本的に追記可能ではないため、諦める必要があります。ただし、データが履歴に移行して「ライブパーティション」から外れるロールオーバー時には、通常通りソートして属性を追加できます

特定のシステムの要件に基づいて、上記のアーキテクチャはリアルタイムエンジンやキャッシュプロセスを追加してアナリティクス(例:VWAP、バーなど)を計算するように拡張できます。これらの値はティッカープラントにパブリッシュしてデータベースプロセスから読み取るか、直接アクセスできます

arch2

これらの技術的詳細についてあまり深入りしたくありませんので、TorQ を使用してこの種のアーキテクチャを設定する方法の詳細を後日フォローアップすることを目指します:

  • データをできるだけ速くリロードおよび再マッピングする方法
  • ロールオーバー
  • 設定と開始をできるだけ簡単にする方法

私は長い結論を好みませんが、少なくとも kdb+ の代替デフォルトアーキテクチャを検討するきっかけになれば幸いです。「no rdb」アーキテクチャはよりシンプルで、スケーラブルで、ほぼ同等の速度です。私はこれが「正しい」方法だと言っているわけではなく、単にもう少し強く検討すべきものだと言っているだけです。このアイデアについて他の意見やさらに議論したい方がいらっしゃいましたら、ぜひお聞かせください!