本文にスキップ
AI News HubLIVE
サイト内リライト5 分で読了

SmithDBの全文検索:オブジェクトストレージ向け転置インデックスの設計

記事の要約

SmithDBは、大規模で深くネストされたJSONドキュメント(エージェントトレース)をオブジェクトストレージに保存しながら、全文検索とJSONフィルタリングを中央値(P50)400ミリ秒のレイテンシで提供します。本記事では、エージェントトレースのインデックス作成における独自の課題、オブジェクトストレージの制約、そして教科書的な実装からバイト予算付き行グループレイアウトへと進化した転置インデックスの第一原理からの設計アプローチを説明します。

SmithDBの全文検索:オブジェクトストレージ向け転置インデックスの設計
誤りを報告

訂正窓口はまだ利用できません。記事情報をコピーして保存できます。

訂正案内
本文へ

SmithDBは、LangChainが提供するエージェントトレースデータの保存とクエリのためのデータベースシステムです。大規模で深くネストされたJSONドキュメントに対して全文検索とJSONフィルタリングをオブジェクトストレージ上で実行し、中央値(P50)レイテンシを400ミリ秒に保つ必要があります。これは従来のログ検索エンジンとは大きく異なります。なぜなら、エージェントトレースデータの個々のドキュメントは非常に大きく、コンテンツフィールド(inputsとoutputs)が通常ほとんどのバイトを占め、サイズは1MB以上、時には非圧縮で数百MBに達することもあるからです。

全文検索自体は新しい問題ではありません。Luceneは20年の歴史があり、TantivyやQuickwitも検索とインデックス作成をオブジェクトストレージに推し進めています。しかし、SmithDBチームは第一原理から再設計することを決めました。エージェントトレースのインデックス作成には2つの独自の課題があるからです。

第一の課題はエージェントトレースデータの特性です。各LangSmithイベントでは、inputsとoutputsフィールドがバイトの大半を占め、通常1MBを超え、非圧縮で数百MBに達するものもあります。これらのコンテンツ列は、ID、タイムスタンプ、その他のメタデータ列よりも数桁大きくなります。さらに、LLMのコンテキストウィンドウが拡大し、エージェントの実行時間が長くなるにつれて、これらのペイロードは増え続けています。これにより、従来の検索インデックスの経済性が逆転します。通常、ログエンジンは数十億の小さなドキュメントをインデックス化し、インデックスサイズはソースデータの約1.25倍です。しかしSmithDBは数十億の巨大なドキュメントをインデックス化し、ソース対インデックスの比率は1:1.9に近くなります。これはつまり、インデックスなしのコンテンツフィルタリングは壊滅的に遅く、単語頻度はジップ分布に従い(少数の単語がほぼすべてのドキュメントに出現し、長尾の単語は1回か2回しか出現しない)、クエリは複数のモード(パス、値、フリーテキスト)をサポートする必要があるということです。

第二の課題はオブジェクトストレージです。SmithDBはすべての永続データをオブジェクトストレージに保存するため、コンピュートは比較的ステートレスであり、ローカルディスクを管理することなくノードを追加してスケールします。クエリのコストは、おおよそ(オブジェクトストレージへのリクエスト数)×(リクエストあたりの読み取りバイト数)に比例します。各オブジェクトストレージリクエストのレイテンシは数十から数百ミリ秒で、リクエストあたりのスループットは限られているため、大きなポスティングリストや位置リストを事前に取得するとクエリ時間が支配される可能性があります。SmithDBの転置インデックスのあらゆる側面は、ストレージレイアウトからクエリ実行に至るまで、これらの制約を考慮して設計されています。

SmithDBのクエリサーフェスは3つの述語ファミリーに集約されます。json_key(パス存在)、json_key_search(キー付き値検索)、search(全文検索)です。json_keyはドキュメントにキーパスが存在するかを確認し、キーパス自体にLIKEパターンをサポートします(例:json_key(inputs, "author.%"))。json_key_searchはキーの値が指定値に一致するかを確認し、単一トークンまたは複数トークンのフレーズ(隣接性を要求)を取ります。searchはテキスト列またはすべてのJSON値に対してクエリを検索します(パスに関係なく)。

転置インデックスはすべての検索ライブラリを支えるデータ構造であり、その中核概念には、用語(JSONパス、キー付き値、テキストトークンなどのインデックス単位)、ポスティングリスト(用語を含むドキュメントIDのソート済みセット)、位置(用語がドキュメント内で出現する位置、フレーズ検索を可能にする)が含まれます。例えば、5つのドキュメントがある場合、インデックスは各用語のエントリを維持し、それを含むドキュメントを指します。search("deep agents")のようなクエリでは、deepとagentsのポスティングリストの交差を取ることで、両方を含むドキュメントIDを取得し、ペイロードスキャンを回避します。位置情報により、search("langsmith engine")がlangsmithとengineが隣接して出現するドキュメントのみに一致するようなフレーズ検索が可能になります。

SmithDBチームは、Tantivyに大きく触発されながらも、直接採用しませんでした。理由は3つあります。Tantivyはmmapを中心に構築され、ローカルディスクとマイクロ秒のランダムI/Oを前提としていますが、SmithDBはオブジェクトストレージ上で約100ミリ秒のラウンドトリップがあり、レイアウトと合体がクエリレイテンシを決定します。Tantivyは独立した検索スタックですが、SmithDBはApache DataFusionとVortexの同じスキャンパイプラインを通じて検索をプッシュダウンしたいと考えています。TantivyのドキュメントIDはセグメントローカルで挿入順に割り当てられ、マージのたびに再番号付けされますが、SmithDBはインデックスがVortexデータファイル内の行位置を直接指す必要があり、変換テーブルを避けます。

VortexはSmithDBが使用する拡張可能な列指向ファイル形式で、プラグ可能なエンコーディングとカスタムファイルレイアウトをサポートします。各読み取りは統計情報を使用して行グループ全体を剪定し、生き残った行をマスクにフィルタリングし、クエリに必要な列のみを投影します。VortexのI/O単位はセグメント(連続する物理バイト範囲)であり、I/Oスケジューラは隣接するセグメント読み取りを1つのリクエストに合体させ、1MBのギャップ内の読み取りを最大16MBのウィンドウでマージするため、順次アクセスパターンが少数のオブジェクトストレージGETにマッピングされます。

最初の転置インデックス実装は、ほぼ教科書的な直訳でした。2つの列(パス用のterm_key、トークン用のterm_value)で3つのクエリ形状すべてを処理し、ポスティングリストをListセルとして、位置をList>として保存しました。Vortexのデフォルト(用語列にFSSTエンコーディング、ポスティングリストと位置にビットパックエンコーディング、クエリ時の剪定のためのゾーンストレージレイアウト)に依存しました。位置(フレーズ検索に必要)は他の列よりも一桁大きく、インデックスをコア実行データとは別のファイルに保存することで、インデックス構築とマージをコア書き込みパスから分離しました。

しかし、規模が拡大すると3つの問題が発生しました。第一に、用語ごとのエンコーディング制御ができないことです。Vortexは列全体のエンコーディングを選択するため、1つの一般的なトークン(agent、langchainなど)がチャンク全体のすべての用語により大きなビット幅を強制し、ビットパッキングの効率が悪くなります。第二に、固定サイズの行グループが用語の偏りに無頓着であることです。固定数の用語を行グループにバッチ処理すると、1つの高頻度用語が1つの行グループを圧縮後100MB以上に押し上げる一方、別の行グループは数MBにとどまることがあります。クエリ時にはこれが1回の巨大なオブジェクトストレージGETになり、マージ時には巨大なメモリ内デコードになります。第三に、マージ時に位置を再形成する必要があることです。2つのセグメントをマージするには、完全なpositions List>をデコードし、内部リストを新しいドキュメント順序に再配置し、すべての外部オフセットを再計算する必要があり、CPU時間と割り当てが圧縮時に急増します。位置がインデックスバイトの70%以上を占めるため、これが圧縮の主要コストでした。

第2版のレイアウトは、組織単位を「行グループあたりN用語」から「バイト予算付き行グループ」に変更し、Vortexのデフォルトに直接依存するのではなく列ごとのバイトレイアウトを自主制御することで、上記の3つの問題を解決しました。詳細はブログの第2部で続きます。

要点と分析を開く

記事インテリジェンス

エンジニア中級

要点

  • SmithDBは、json_key(パス存在)、json_key_search(キー付き値)、search(全文検索)の3つのクエリ形状をサポートします。
  • エージェントトレースのペイロードは巨大(多くの場合1MB以上)で、単語頻度はジップ分布に従い、ソース対インデックスの比率は1:1.9になります。
  • オブジェクトストレージはリクエストあたり約100msの高いレイテンシと限られたスループットを課すため、IOリクエストを最小化するレイアウト最適化が必要です。
  • 初期の転置インデックス設計は教科書的なアプローチを直訳したもので、用語ごとのエンコーディング制御、固定サイズ行グループ、マージ時の位置再形成コストという問題に直面しました。
  • v2レイアウトは、バイト予算付き行グループと列ごとのバイトレイアウトの自主制御により、Vortexのデフォルトから脱却してこれらの問題を解決します。

要点と分析は自動生成され、誤りを含む場合があります。原典をご確認ください。