数千件規模の検索を一括処理するCortex Search Batchの全体像と基本設計

目次

数千件規模の検索を一括処理するCortex Search Batchの全体像と基本設計

Snowflakeが提供するCortex Searchは、テーブル内の非構造化テキストデータに対してベクトル検索とキーワード検索を組み合わせたハイブリッド検索を実行できるフルマネージドサービスです。チャットボットのRAGエンジンやエンタープライズ検索バーの裏側で、低レイテンシの検索体験を支える役割を担ってきました。しかし、エンティティ解決やカタログマッピングのように数千〜数万件のクエリを一度に処理したいオフラインワークロードでは、1件ずつ検索を繰り返す従来方式ではボトルネックが避けられません。この課題を根本から解決するために登場したのが、テーブル関数CORTEX_SEARCH_BATCHを使ったバッチ検索機能です。なお、この機能は2026年3月時点でPreview(プレビュー)ステータスとして提供されています。本記事では、バッチ検索の内部構造から導入手順、ベンチマーク結果、コスト設計、実装パターンまでを体系的に解説し、実務での導入判断に必要な情報をすべて網羅していきましょう。

CORTEX_SEARCH_BATCHが解決するSEARCH_PREVIEWの大量処理限界

Cortex Search Serviceに対してSQLから検索を実行する標準的な手段は、SNOWFLAKE.CORTEX.SEARCH_PREVIEW関でしょう。この関数は単一のクエリ文字列をリテラルとして受け取り、指定したサービスに対して検索結果を返します。テスト用途や少数のクエリであれば十分に機能する一方、大きな制約が存在しているのが現状でしょう。SEARCH_PREVIEWは定数文字列のみを受け付けるため、テーブルのカラム値を動的に渡すことができません。つまり、1000件の検索を実行するには1000個のSELECT文をUNION ALLで連結するか、ストアドプロシージャでループ処理を記述する必要があります。

この方式では、クエリ数が増えるほどSQLの記述量が爆発的に増加し、保守性も著しく低下します。さらに、SEARCH_PREVIEWはインタラクティブ検索向けに設計されており、公式ドキュメントで明記されている1サービスあたり20QPSの上限を超えるとHTTP 429が返されスロットリングが発生するのが実情でしょう。10件程度であれば問題になりませんが、100件を超えた段階で実行時間が線形的に増加し、1万件では約58分かかると推定されるため、バッチ処理のワークロードへの適用は現実的ではないでしょう。

CORTEX_SEARCH_BATCHはこの限界を根本的に解消するテーブル関数として設計されました。テーブルに格納した検索クエリをLATERAL句で一括送信し、バッチ専用の追加コンピュートリソースを活用して並列処理を実行します。同一のCortex Search Serviceインデックスをそのまま利用できるため、バッチ用に別のオブジェクトを作成する手間は不要です。結果として、1万件のクエリを約73秒で処理でき、インタラクティブ方式と比較して最大47倍のスループット向上が確認されました。

テーブル関数とLATERAL構文で実現するバッチクエリの基本構造

CORTEX_SEARCH_BATCHの呼び出し構文は、SQLのLATERALテーブル関数として設計された仕組みにあたります。基本的な実行パターンは、検索クエリを格納したテーブルとLATERAL句を組み合わせる形式です。具体的には、クエリテーブルの各行に対してCORTEX_SEARCH_BATCHが呼び出され、指定した件数の検索結果が展開される仕組みとなっているのが実情でしょう。

SELECT q.query_text, s.* FROM my_queries AS q, LATERAL CORTEX_SEARCH_BATCH(service_name => 'DB.SCHEMA.SERVICE', query => q.query_text, limit => 5) AS s;

この構文では、service_nameパラメータに対象のCortex Search Serviceの完全修飾名を指定し、queryパラメータにクエリテーブルのカラムを渡します。limitパラメータは各クエリあたりの返却件数を制御するもので、たとえばlimit=5で1000件のクエリを実行すると最大5000行の結果が返されることになしょう。SEARCH_PREVIEWがリテラル文字列しか受け付けないのに対し、CORTEX_SEARCH_BATCHはカラム参照を直接渡せるため、動的なバッチ処理が自然なSQL構文で記述できるようになりました。

クエリテーブルは通常のテーブルでも一時テーブルでも問題ありません。事前にCREATE TEMPORARY TABLEで検索対象のテキストをINSERTしておき、バッチ処理の完了後に自動的に破棄させる運用が一般的です。この構造により、ハードコードされたUNION ALLの羅列から解放され、可読性と保守性の高いバッチ検索が実現できるでしょう。

インタラクティブ検索と同一インデックスを共有できる運用上の利点

CORTEX_SEARCH_BATCHを利用するにあたって重要な設計上の特徴は、既存のCortex Search Serviceをそのまま使える点にあります。バッチ検索用に別のサービスやインデックスを新規作成する必要はなく、インタラクティブ検索で使用しているサービスに対してバッチクエリを投入する形で運用できます。これにより、データの二重管理やインデックスの同期という追加の運用負荷を回避できるのが大きなメリットでしょう。

さらに注目すべきは、インタラクティブクエリとバッチクエリを同一サービスに対して並行実行しても、互いのパフォーマンスに影響を与えない点です。Snowflakeの公式ドキュメントでは、それぞれのクエリタイプに対して別々のコンピュートリソースが割り当てられると説明されています。つまり、ユーザー向けのリアルタイム検索アプリケーションを稼働させたまま、裏側で大量のバッチ検索ジョブを同時に走らせることができるようになっているのが実情でしょう。

この設計はインフラ管理の観点でも大きなメリットをもたらします。サービスの作成・維持コストは1つ分で済み、TARGET_LAGの設定やエンベディングモデルの選択も一元管理できるからにほかなりません。開発チームとデータサイエンスチームが同じインデックスを共有しつつ、用途に応じてインタラクティブとバッチを使い分けるワークフローを構築できるため、組織横断的なデータ活用を効率的に推進できるようになるでしょう。

ウェアハウスサイズに依存しないスループット設計の技術的背景

通常のSnowflakeワークロードでは、ウェアハウスのサイズを大きくするほど処理速度が向上するのが一般的です。しかし、CORTEX_SEARCH_BATCHのスループットはウェアハウスサイズに依存しないという特殊な設計になっています。SMALLウェアハウスでもX-LARGEウェアハウスでも、バッチ検索の処理速度に差は生じません。この仕様は、バッチ検索がウェアハウスのコンピュートリソースではなく、Cortex Search Service側に確保された専用リソースで処理されることに起因しています。

ウェアハウスが担う役割は、クエリテーブルのスキャンとバッチ関数の呼び出しに限定されます。実際の検索処理であるベクトル類似度計算やキーワードマッチング、セマンティックリランキングは、すべてCortex Search Serviceの内部コンピュートで実行される仕組でしょう。そのため、コスト最適化の観点では、バッチ検索の実行にはSMALLサイズのウェアハウスを選択するのが合理的な判断といえるでしょう。

一方、ウェアハウスサイズが影響するのはCortex Search Serviceの作成時とリフレッシュ時です。ソースクエリのマテリアライズ処理にウェアハウスのコンピュートが使われるため、大規模データセットに対してサービスを構築する場合は、MEDIUMサイズまでのウェアハウスが推奨されています。バッチ検索の実行コストと、サービス構築・更新コストを明確に分離して管理することが、想定外のクレジット消費を防ぐ鍵となるでしょう。

2026年3月時点で確認できる対応リージョンと利用前提条件の整理

Cortex Search Batchを利用するためには、いくつかの前提条件を満たす必要があります。まず重要な点として、CORTEX_SEARCH_BATCH機能は2026年3月時点でPreview(プレビュー)ステータスであり、本番ワークロードへの適用には注意が求められるでしょう。Snowflakeアカウントに対してACCOUNTADMINロール、または十分な権限を持つカスタムロールが必要です。具体的には、Cortex Search Serviceの作成にはSNOWFLAKE.CORTEX_USERデータベースロールまたはSNOWFLAKE.CORTEX_EMBED_USERデータベースロールの付与が求められるでしょう。エンベディング生成機能を利用するため、これらのロールが未付与の場合はサービス作成時にエラーが発生してしまうでしょう。

エンベディングモデルの利用可能リージョンにも注意が必要です。snowflake-arctic-embed-l-v2.0をはじめとするモデルは、すべてのクラウドリージョンで利用できるわけではなく、Snowflakeの公式ドキュメントでリージョン別の対応状況が公開されています。自社のSnowflakeアカウントが所在するリージョンでCortex Searchがサポートされているかを事前に確認することが、導入計画における第一歩となるでしょう。

また、ソーステーブルの行数には上限があり、マテリアライズされたクエリ結果が1億行を超えるとサービスの作成がエラーになります。この上限を超える場合はSnowflakeのアカウントチームへの相談が必要です。さらに、バッチ検索の同時実行数に制限はないと公式に明記されていますが、スループットはインデックスに登録されたデータ量やクエリの複雑度によって変動するため、本番導入前にはベンチマークテストの実施が推奨されるでしょう。2026年3月時点のガイドでは、10万件のWikipedia記事データセットに対するバッチ検索の動作が検証済みとなっています。

ハイブリッド検索エンジンの内部構造とバッチ処理で高速化が実現する仕組み

Cortex Search Batchの性能を正確に理解するためには、その基盤となるCortex Searchエンジン自体の内部アーキテクチャを把握する必要があります。単なるキーワード検索でもなく、純粋なベクトル検索でもない、ハイブリッド方式がCortex Searchの根でしょう。この章では、検索パイプラインの各段階を分解し、バッチ処理がどの工程で並列化されるのかを明らかにしたうえで、エンベディングモデルの選択やTARGET_LAG設定がバッチ検索の品質と鮮度にどう影響するかを掘り下げます。

ベクトル検索とキーワード検索を組み合わせるハイブリッドランキングの流れ

Cortex Searchに送信されたクエリは、まず2つの異なる経路で処理されるでしょう。1つ目はベクトル検索で、クエリテキストをエンベディングモデルによって数値ベクトルに変換し、インデックス内のドキュメントベクトルとのコサイン類似度を計算して上位候補を取得する流れとなっています。2つ目はキーワード検索で、ステミングやレンマタイゼーション、ドメイン固有の書き換えを経てトークン化されたクエリ語句と、インデックス内のテキストを照合する従来型の全文検索にあたしょう。

この2系統の検索結果はそれぞれ独立したスコアを持ち、最終的にマージされて統合ランキングが生成されます。ベクトル検索は「意味的に近い文書」を拾い上げるのに優れ、キーワード検索は「特定の用語が正確に含まれる文書」を漏らさず返す強みがあるのが実情でしょう。たとえば「Snowflake」というクエリに対して、ベクトル検索は「冬」や「降雪」に関する記事も候補に含めますが、キーワード検索はSnowflakeという語句そのものが出現する記事を確実に返却します。

この二重構造により、タイポや同義語による検索漏れを防ぎつつ、完全一致が重要なケースにも対応できる高品質な検索体験が提供されるでしょう。バッチ検索においても、このハイブリッドパイプラインはクエリごとに同様に適用されるため、インタラクティブ検索と同等の検索品質が大量処理時にも維持されるよう設計されています。

セマンティックリランキングが検索精度を引き上げる3段階パイプライン

ハイブリッド検索で取得された候補ドキュメント群は、そのまま返却されるのではなく、最終段階でセマンティックリランキングという処理を通過するのが一般的でしょう。これはニューラルネットワークを用いた再ランク付けの工程で、クエリとドキュメントの意味的な関連度をより精密に評価し、初期ランキングを修正するものです。つまり、Cortex Searchの検索パイプラインはベクトル検索、キーワード検索、セマンティックリランキングの3段階のパイプラインで成り立っているのが現状でしょう。

リランキングの効果は検索品質の測定可能な向上として現れますが、レイテンシへの影響もあります。Snowflakeの公式情報によると、リランキングの追加により1クエリあたり100〜300ミリ秒程度のレイテンシ増加が見込まれるでしょう。インタラクティブ検索では、ユーザー体感のレスポンス速度を優先してリランキングを無効化する選択肢も用意されており、scoring_configrerankerパラメータを"none"に設定することで制御できるようになっています。

バッチ検索のユースケースでは、レイテンシよりも検索品質が重視される場面が多いため、リランキングを有効にしたまま運用するのが一般的でしょう。エンティティ解決や重複排除の精度は、リランキングの有無によって結果が大きく変わる可能性があるため、品質とコストのトレードオフを検証したうえで判断するのが望ましいでしょう。

バッチ専用の追加コンピュートリソースが並列処理を加速する内部機構

CORTEX_SEARCH_BATCHがインタラクティブ検索と比較して圧倒的なスループットを実現する理由は、バッチジョブに対して専用の追加コンピュートリソースが割り当てられる点にあります。インタラクティブ検索のREST APIやPython SDKは、各クエリを個別のリクエストとして処理するトランザクショナルな設計です。1件あたりの応答速度は約300ミリ秒と高速ですが、大量のクエリを逐次的に処理するとスロットリングが発生し、毎秒約2.9件で頭打ちとなってしまいるのが現状でしょう。

一方、CORTEX_SEARCH_BATCHはクエリ群をまとめて送信する設計になっており、Cortex Search Service側が複数のクエリを並列に処理するためのリソースを自動的にスケールアップします。この結果、クエリ数が増えるほどスループット効率が向上し、1万件の検索では毎秒約136件の処理速度に到達するというベンチマーク結果が報告されているのが特徴でしょう。起動時のオーバーヘッドが存在するため、100件未満の少量クエリではインタラクティブ検索と同等かやや遅くなりますが、500件を超えたあたりからバッチ検索の優位性が明確になる特性を備えています。

また、同一サービスに対して複数のバッチクエリを同時実行することも可能であり、並列実行によってさらに高いスループットを達成できると公式に案内されているのが特徴でしょう。つまり、10万件規模のクエリを1万件ずつに分割して並列投入する運用も現実的な選択肢といえるでしょう。

TARGET_LAGとインデックス自動更新がバッチ結果の鮮度を左右する仕組み

Cortex Search Serviceはソーステーブルの変更を自動的に検出し、インデックスを更新する仕組みを備えています。この更新頻度を制御するのがTARGET_LAGパラメータで、サービス作成時に'1 hour''1 day'といった値で指定します。TARGET_LAGが1時間に設定されていれば、ソーステーブルに新しいデータが挿入されてから最大1時間以内にインデックスが更新され、バッチ検索の結果にも反映される仕組みとなっているのが実情でしょう。

バッチ検索のユースケースでは、リアルタイム性よりもデータの完全性が重視される傾向があります。たとえば、夜間のカタログマッピングジョブでは、日中に蓄積されたデータが検索時点でインデックスに含まれていれば十分であり、TARGET_LAGを'1 day'に設定することでリフレッシュコストを抑制でくことになるでしょう。一方、頻繁にデータが更新される環境でバッチ検索の結果鮮度を重視する場合は、TARGET_LAGを短く設定する必要がありますが、リフレッシュの頻度が増えることでコンピュートコストも比例して増加する点には留意が求められるでしょう。

インデックスのリフレッシュにはフルリフレッシュとインクリメンタルリフレッシュの2つのモードが存在します。インクリメンタルリフレッシュは前回の更新以降の差分のみを処理するため効率的ですが、ソーステーブルでCHANGE_TRACKINGが有効になっていることが前提です。バッチ検索の品質を維持しながらコストを最適化するには、データ更新の頻度とTARGET_LAGのバランスを実運用データで検証するプロセスが欠かせないでしょう。

snowflake-arctic-embed系モデル選択がバッチ検索精度に与える影響の比較

Cortex Search Serviceを作成する際には、テキストのベクトル化に使用するエンベディングモデルを選択できます。デフォルトモデルはsnowflake-arctic-embed-m-v1.5ですが、より高精度なモデルとしてsnowflake-arctic-embed-l-v2.0などの大型バリアントも利用可能です。モデルの選択はサービス作成後に変更できないため、最初の設計段階で慎重に決定しなければなりません。

モデル名 出力次元数 言語対応 コンテキスト窓 100万トークン単価
snowflake-arctic-embed-m-v1.5(デフォルト) 768 英語のみ 512トークン 0.03クレジット
snowflake-arctic-embed-l-v2.0 1024 多言語対応 512トークン 0.05クレジット
snowflake-arctic-embed-l-v2.0-8k 1024 多言語対応 8192トークン 0.05クレジット
voyage-multilingual-2 1024 多言語対応 32000トークン 0.07クレジット

デフォルトモデルのsnowflake-arctic-embed-m-v1.5は英語専用の110Mパラメータモデルであり、インデクシング速度が最も高速である一方、日本語などの非英語テキストには対応していません。多言語対応が必要な場合はsnowflake-arctic-embed-l-v2.0(568Mパラメータ)以上のモデルを選択する必要があります。バッチ検索で大量のクエリを処理する場合、個々のクエリに対する検索結果の品質が最終的なタスク精度を大きく左右します。たとえば、エンティティ解決で表記揺れの激しい企業名をマッチングする場合、大型の多言語モデルは微妙なニュアンスの違いを捉える能力に優れており、マッチ率の向上が期待できるでしょう。

ただし、エンベディングモデルの変更にはサービスの再作成が必要であり、大規模データセットでは再構築に相応の時間とコストがかかります。そのため、本番導入前に小規模なデータセットで複数モデルの精度比較を実施し、バッチ検索の品質要件に適合するモデルを選定するプロセスを踏むことが推奨されます。モデルごとのクレジット消費単価も異なるため、100万トークンあたりのコストをSnowflakeのサービス消費テーブルで確認しておくことも欠かせないでしょう。

エンティティ解決から重複排除まで実務で効くバッチ検索6つの適用領域

CORTEX_SEARCH_BATCHの真価は、大量の検索クエリを高速に処理できるスループット性能を活かした実務タスクへの適用にあります。Snowflakeの公式ガイドでも複数のユースケースが紹介されていますが、いずれも共通するのは「大量のレコードを既存の検索インデックスに対して照合し、類似度スコアに基づいてマッチングやグルーピングを行う」というパターンです。この章では、代表的な6つの適用領域について、課題構造とバッチ検索による解決アプローチを具体的に掘り下しょう。

表記揺れを含む顧客名寄せで5万件を数分処理するエンティティ解決の実例

エンティティ解決は、CORTEX_SEARCH_BATCHの最も代表的なユースケースの一つです。CRM、請求システム、サポートチケット、マーケティングオートメーションなど、複数のシステムに散在する同一顧客のレコードを統合する作業では、表記揺れへの対処が最大の課題になしょう。同じ企業が「Acme Corporation」「Acme Corp」「ACME Inc.」「Acme (US)」といった複数の表記で登録されているケースは珍しくなく、単純な文字列一致では名寄せに対応しきれないのが実情でしょう。

バッチ検索を活用するアプローチでは、まず正規化されたマスター顧客データをCortex Search Serviceのインデックスとして構築します。次に、名寄せ対象の顧客名一覧をクエリテーブルとしてバッチ検索に投入し、各レコードに対するセマンティック類似度の高い候補を取得します。ハイブリッド検索のおかげで、スペルの違いや略称を超えた意味的なマッチングが可能であり、従来のファジーマッチングでは検出できなかった対応関係も発見できる点が特長となっているのが実情でしょう。

返却される類似度スコアを活用することで、高スコアのマッチは自動統合、中程度のスコアは人手レビューという二段構えのワークフローを構築できます。5万件の顧客レコードに対するバッチ検索は数分で完了するため、従来であれば数日がかりだった名寄せ作業を大幅に短縮できるのが最大のメリットといえるでしょう。

供給元と自社SKUの意味的照合で属人作業を排除するカタログマッピング

小売業やEC事業では、複数のサプライヤーからの商品フィードを自社のカタログ体系にマッピングする作業が日常的に発生します。サプライヤーごとに商品名の表記規則が異なり、たとえば同じテレビ製品が「Samsung 65in QLED TV 4K」と「Samsung QN65Q80C 65-Inch Smart TV」という全く異なる商品名で送られてきます。この対応付けを人手で行うと、新規サプライヤーのオンボーディングのたびに膨大な工数が発生し、スケーラビリティの壁にぶつかってしまいがちでしょう。

カタログマッピングにCORTEX_SEARCH_BATCHを適用する場合、自社の正規商品カタログをCortex Search Serviceのインデックスとして登録し、サプライヤーの商品説明文をクエリテーブルとしてバッチ検索にかけます。ハイブリッド検索がベクトル類似度とキーワードマッチの両面で候補を返すため、商品説明の書き方が大きく異なっていても、意味的に正しいマッチングが期待できるようになっています。

この手法の最大の強みは、属人的な判断に依存していたマッピング作業を体系的・再現可能なプロセスに転換できる点にあるのが実情でしょう。数千SKUの一括マッピングを一度のバッチクエリで処理できるため、サプライヤーの追加に伴うマッピング工数が線形的に増加する問題を解消します。スコアの閾値を適切に設定すれば、高確信度のマッチは自動確定し、低確信度のものだけを担当者が確認する運用フローを構築することもできるようになっているのが実情でしょう。

類似スコアに閾値を設けて自動マージと人手確認を分離する重複排除設計

CRMやマスターデータ管理において、重複レコードの存在はレポーティングの信頼性を損ない、営業チームの生産性を低下させる原因となります。「John Smith at Snowflake」が微妙な表記違いで3つの別レコードとして存在するような状況では、同一顧客への重複アプローチや不正確な売上集計が発生するのが一般的でしょう。従来のルールベースの重複検出では、名前の表記揺れや住所のフォーマット差異に対応しきれないケースが少なくありませんでした。

CORTEX_SEARCH_BATCHによる重複排除では、データベース内の各レコードを自分自身を含むインデックス全体に対して検索し、類似度の高い候補を取得します。自身との完全一致を除外し、スコアが一定の閾値を超えるペアを重複候補として抽出する仕組みです。セマンティック検索により、単なる文字列の類似度だけでなく、意味的な同一性も考慮した判定ができるようになしょう。

運用設計においては、類似スコアの閾値を2段階に分けるアプローチが効果的です。たとえばスコア0.95以上は高確信度として自動マージの対象とし、0.80〜0.95は中確信度としてレビューキューに送る運用が考えられるでしょう。バッチ検索であれば、数万件のレコードに対する全組み合わせの類似度検査を短時間で完了できるため、定期的なデータクレンジングのジョブとしてスケジュール実行する運用も視野に入るでしょう。

サポートチケットやドキュメントを自動分類するコンテンツタギングの手法

大量のサポートチケットやナレッジベース記事、社内ドキュメントに対してカテゴリタグを付与する作業は、人手で行うと時間がかかるうえに一貫性が担保されにくい業務です。キーワードルールによる自動分類では文脈を考慮できず、誤分類が頻発するという問題も生じかねません。CORTEX_SEARCH_BATCHを活用したコンテンツタギングは、こうした課題に対するスケーラブルな解決策を提供してくれるでしょう。

具体的には、カテゴリ体系やタクソノミーの定義文書をCortex Search Serviceのインデックスとして登録し、分類対象のコンテンツをクエリテーブルとしてバッチ検索します。各コンテンツに対して最も類似度が高いカテゴリが返却されるため、上位のマッチ結果をタグとして自動付与する仕組みです。カテゴリの説明文にメタデータを追加しておけば、検索精度をさらに向上させることもでくことになるでしょう。

この手法はCortex LLM Functionsと組み合わせることで、さらに高度な分類が可能になります。Cortex Searchで候補カテゴリを絞り込み、その候補をLLMのプロンプトに渡して最終分類を判定させるパイプラインでは、LLMに送るカテゴリ数が大幅に削減されるため、分類コストを最大51倍削減できたという報告もあるのが実情でしょう。バッチ検索による候補絞り込みがコスト効率のボトルネック解消に直結する好例といえるでしょう。

類似商品レコメンドや顧客リスト照合に応用するバッチ検索の発展パターン

バッチ検索の適用範囲はマッチングや分類だけにとどまりません。類似アイテムの発見やオーディエンスマッチングといった応用パターンにも有効となるでしょう。類似商品レコメンドでは、商品説明文をインデックスに登録し、各商品を自身以外のインデックスに対してバッチ検索することで、「この商品に似た商品」のリストを一括生成できます。専用の推薦エンジンを構築しなくても、カタログ更新のたびにバッチクエリを再実行するだけでレコメンド結果を更新できるのが利点となっています。

オーディエンスマッチングは、2つの顧客リスト間の重複を特定する用途で活用されるでしょう。パートナー企業との共同マーケティングにおいて、生のPII(個人識別情報)を共有せずにハッシュ化またはトークン化された識別子で照合を行う場面で効果を発揮します。一方のリストをインデックスとして登録し、もう一方をバッチクエリとして投入すれば、重複候補のスコアリストが返却される流れとなっているのが実情でしょう。

これらの発展パターンに共通するのは、「大量のテキストレコードを既存インデックスに対して一括照合し、類似度スコアに基づいて後続処理を分岐させる」という設計思想です。CORTEX_SEARCH_BATCHが提供するのは汎用的な類似検索のバッチインターフェースであり、その上にどのようなビジネスロジックを構築するかは利用者のユースケース次第で柔軟に設計でくことになるでしょう。バッチ検索を単なる検索機能ではなく、データパイプラインの中核コンポーネントとして位置づけることで、活用の幅が大きく広がっていくでしょう。

Cortex Search Service構築からバッチ実行まで最短で動かす具体的な導入手順

CORTEX_SEARCH_BATCHの概念とユースケースを理解したところで、実際にバッチ検索を動かすための具体的な手順に進みます。Cortex Search Serviceの作成からバッチクエリの実行、結果の後処理まで、一連の工程をSQL中心で解説します。Python SDKを使った実装は後述の章に譲り、ここではSnowsightのワークシートだけで完結する最短パスに焦点を当しょう。

データベースとウェアハウスの初期設定で押さえるべき権限とロールの要件

Cortex Search Serviceを作成するためには、まず適切な権限を持つロールで作業環境を整備する必要があります。最も確実な方法はACCOUNTADMINロールを使用することですが、本番環境ではセキュリティの観点からカスタムロールの使用が望ましいでしょう。カスタムロールを利用する場合、SNOWFLAKE.CORTEX_USERデータベースロールまたはSNOWFLAKE.CORTEX_EMBED_USERデータベースロールがそのロールに付与されている必要があります。

ウェアハウスの作成については、バッチ検索のスループットがウェアハウスサイズに依存しないことを踏まえ、SMALLサイズで十分です。ただし、Cortex Search Serviceの初回構築時にはソースクエリのマテリアライズにウェアハウスが使われるため、大規模なソーステーブルを扱う場合はMEDIUMサイズまでの引き上げを検討してもよいでしょう。AUTO_SUSPENDとAUTO_RESUMEを有効にしておけば、バッチ処理の前後でウェアハウスが自動的に起動・停止し、不要なコストの発生を防げるようになっています。

初期設定のSQL例として、データベース、スキーマ、ウェアハウスの作成と権限付与の一連の流れを実行します。これらの基盤オブジェクトは一度作成すれば再利用できるため、初回のみの作でしょう。既存のデータベースやウェアハウスを流用する場合は、USE文でコンテキストを切り替えるだけで次のステップへ進められるでしょう。

CREATE CORTEX SEARCH SERVICEで指定する主要パラメータ5項目の設定例

CREATE CORTEX SEARCH SERVICE文は、Cortex Searchの検索インデックスを構築するための中核的なDDLコマンドです。このコマンドで指定する主要パラメータは5つあり、それぞれがバッチ検索の品質、コスト、鮮度に直接的な影響を及ぼすものとなっています。

パラメータ 設定例 役割 バッチ検索への影響
ON句(検索対象カラム) ON raw_text 全文検索の対象テキストカラム 検索品質の基盤となる
ATTRIBUTES ATTRIBUTES title, region フィルタリングおよび返却対象カラム 結果の絞り込み精度に影響
WAREHOUSE WAREHOUSE = my_wh 構築・リフレッシュ用ウェアハウス 構築速度とリフレッシュコスト
TARGET_LAG TARGET_LAG = ‘1 hour’ インデックス更新の目標遅延 バッチ結果の鮮度を決定
EMBEDDING_MODEL snowflake-arctic-embed-l-v2.0 テキストのベクトル化モデル 検索精度とクレジット消費単価

AS句にはソースクエリを記述し、インデックスに含めるデータの範囲を定義するのが一般的でしょう。単純なSELECT文だけでなく、JOINやWHERE句を使った複雑なクエリも指定可能です。ただし、マテリアライズされた結果が1億行を超えるとサービス作成がエラーになるため、対象データ量を事前に確認しておくことが重要になしょう。EMBEDDING_MODELを省略した場合はデフォルトのsnowflake-arctic-embed-m-v1.5が適用されますが、バッチ検索の精度要件に応じて明示的に指定しておくことが望ましいでしょう。

CHANGE_TRACKINGの有効化とインクリメンタル更新で失敗しない前準備

Cortex Search Serviceの効率的な運用には、インクリメンタルリフレッシュの活用が欠かせません。フルリフレッシュはソースデータ全体のエンベディングを再計算してインデックスを完全に再構築する方式で、大規模データセットでは時間もコストも大きくなります。インクリメンタルリフレッシュは前回の更新以降に変更された差分のみを処理するため、はるかに効率的な方式といえるでしょう。

インクリメンタルリフレッシュを利用するためには、ソーステーブルでCHANGE_TRACKINGが有効になっている必要があります。これはSnowflakeのテーブルプロパティで、行レベルの変更を効率的に検出する機能を提供するものです。テーブル作成後にALTER TABLE my_table SET CHANGE_TRACKING = TRUE;で有効化でくことになるでしょう。なお、Snowflake Marketplaceから取得した共有データベースのテーブルに対しては、直接CHANGE_TRACKINGを有効化できないため、ローカルデータベースにコピーしてからサービスを作成する必要がある点に注意が求められるでしょう。

PRIMARY KEY句の設定も効率的なリフレッシュに寄与します。主キーを指定したサービスでは、最適化されたリフレッシュパスが利用され、コストとレイテンシの大幅な削減が見込めます。主キーカラムはTEXT型である必要があり、組み合わせの値がユニークでなければなりません。重複する主キーを持つ行はインデックスから除外されるため、ソースデータの品質管理もあわせて行うのが望ましいでしょう。

LATERAL CORTEX_SEARCH_BATCHの基本構文とクエリテーブルの作成手順

Cortex Search Serviceの構築が完了したら、いよいよバッチ検索を実行します。まず、検索クエリを格納するテーブルを作成します。一時テーブルを使う方法が最もシンプルで、バッチ処理の完了後に自動的にクリーンアップされるため管理負荷を抑えられるのが利点でしょう。

  1. 検索クエリを格納する一時テーブルを作成する:CREATE OR REPLACE TEMPORARY TABLE my_queries (query_text VARCHAR);
  2. 検索対象のテキストをINSERT文で投入する:INSERT INTO my_queries VALUES ('検索クエリ1'), ('検索クエリ2'), ...;
  3. LATERAL句でCORTEX_SEARCH_BATCHを呼び出す:SELECT q.query_text, s.* FROM my_queries AS q, LATERAL CORTEX_SEARCH_BATCH(service_name => '完全修飾サービス名', query => q.query_text, limit => 5) AS s;

この3ステップをひとまとめに実行することで、バッチ検索の結果が即座に返却されます。service_nameにはデータベース名・スキーマ名を含む完全修飾名を指定することが推奨されます。limitパラメータは各クエリあたりの返却件数を制御し、1から指定可でしょう。既存のテーブルからクエリを生成する場合は、CREATE TABLE AS SELECT文で直接クエリテーブルを作成することもでき、たとえば既存テーブルのタイトルカラムをランダムに1000件抽出してバッチ検索にかけるといった使い方もできるようになっています。

実行結果のJSON展開とFLATTENによる後続分析用テーブルへの変換方法

CORTEX_SEARCH_BATCHの実行結果は、クエリテーブルの各行に対して指定した件数分の検索結果が展開された形で返されるでしょう。結果のカラムにはCortex Search Serviceで定義したATTRIBUTESカラムや検索対象カラムのデータが含まれ、そのまま後続の分析や加工に活用できるようになっています。単純なSELECT結果として参照する場合はこのままで問題ありません。

一方、SEARCH_PREVIEW関数を使った場合はJSON形式で結果が返されるため、PARSE_JSONFLATTENを組み合わせたJSON展開処理が必要です。SEARCH_PREVIEWの結果をテーブル形式に変換するには、FLATTEN関数のinputパラメータにPARSE_JSONで解析したresultsフィールドを渡し、各結果行のキーを::VARCHARでキャストして取り出します。CORTEX_SEARCH_BATCHではこのJSON展開が不要なため、後処理のSQL記述が大幅に簡素化される利点も見逃せないでしょう。

バッチ検索の結果を永続テーブルに保存する場合は、CREATE TABLE result_table AS SELECT ...の形式でバッチ検索の出力をそのまま格納できます。これにより、エンティティ解決の候補リストや重複排除のレビュー対象をテーブルとして永続化し、後続のBI分析やデータ品質管理のワークフローに組み込むことが容易になります。バッチ検索は1回の実行で結果が確定するため、冪等性のある処理としてスケジュールジョブへの組み込みにも適した構造といえるでしょう。

100件と1万件で47倍差が出るスループット性能の実測ベンチマーク比較

CORTEX_SEARCH_BATCHの性能特性を把握するうえで、実測データに基づくベンチマーク結果は最も説得力のある判断材料になります。Snowflakeの公式ガイドでは、10万件のWikipedia記事データセットに対するCortex Search Serviceを対象に、インタラクティブ検索とバッチ検索の処理時間・スループットを複数の規模で比較した結果が公開されています。この章では、その結果を分析し、バッチ検索の導入判断に必要な性能の境界線を明確にするのが一般的でしょう。

100件処理ではバッチとインタラクティブが同等になる起動オーバーヘッドの正体

ベンチマーク結果において最も注目すべき点は、100件のクエリを処理した場合にインタラクティブ検索が約34.9秒、バッチ検索が約35.1秒とほぼ同等の処理時間を記録していることです。バッチ検索のほうがわずかに遅いという結果は、直感に反するように見えるかもしれませんが、これはバッチ処理特有の起動オーバーヘッドに起因するものでしょう。

CORTEX_SEARCH_BATCHはバッチジョブを開始する際に、専用コンピュートリソースの確保やクエリテーブルの読み込み、並列処理のスケジューリングといった初期化処理を行います。このブートストラップコストは固定的な性質を持ち、クエリ数に関係なく一定の時間がかかります。100件程度の少量クエリでは、この固定コストがクエリ1件あたりの処理効率向上を相殺してしまうため、インタラクティブ検索と同等の速度にとどまる結果となっているのが実情でしょう。

この結果から導き出される実務上の指針は明確です。100件未満のクエリに対しては、インタラクティブ検索(Python SDKまたはREST API)を使うほうが効率的であり、バッチ検索への切り替えは不要と判断でくことになるでしょう。バッチ検索の真の威力が発揮されるのは、起動オーバーヘッドが全体の処理時間に占める割合が十分に小さくなる500件以上のスケールからです。

500件超でバッチが3倍速になるクロスオーバーポイントの発生条件

クエリ数を500件に増やすと、バッチ検索の優位性が顕著になり始しょう。ベンチマーク結果では、インタラクティブ検索が2分55秒(175秒)かかるのに対し、バッチ検索は56秒で完了しており、約3.1倍のスピードアップが確認されました。この500件付近がいわゆるクロスオーバーポイント、すなわちバッチ検索がインタラクティブ検索の処理時間を下回る転換点です。

クロスオーバーポイントが500件付近で発生する理由は、バッチ検索の起動オーバーヘッドがクエリ数の増加によって償却される構造にあります。インタラクティブ検索は1クエリあたり約350ミリ秒の固定レイテンシを持ち、クエリ数に対して線形的に処理時間が増加するのが一般的でしょう。一方、バッチ検索は並列処理によって追加クエリの処理コストが逓減するため、スケールするほど1クエリあたりの実効コストが下がっていく構造となっています。

ただし、クロスオーバーポイントの正確な位置はインデックスサイズやクエリの複雑度によって変動する可能性がある点に留意が必要となるでしょう。ベンチマーク結果は10万件のWikipedia記事データセットに基づいており、自社のデータ特性に応じた検証を推奨します。目安として、100件以下であればインタラクティブ、500件以上であればバッチを選択し、100〜500件の範囲では両方を試してみるというアプローチが実務的に合理的な判断といえるでしょう。

1000件処理で約6倍・1万件で約47倍に達するスループット拡大の実測値

クエリ数の増加に伴い、バッチ検索のスピードアップ倍率は劇的に拡大していきます。1000件のクエリでは、インタラクティブ検索が5分59秒(359秒)を要するのに対し、バッチ検索は62秒で完了しており、約5.8倍の高速化が確認されました。さらに1万件に到達すると、バッチ検索は約73.6秒で処理を完了し、インタラクティブ検索の推定処理時間約58分に対して約47倍のスピードアップが実現されました。

クエリ数 インタラクティブ処理時間 バッチ処理時間 スピードアップ倍率
100件 34.9秒 35.1秒 1.0倍
500件 2分55秒 56秒 3.1倍
1,000件 5分59秒 62秒 5.8倍
5,000件 約29分(推定) 約68秒 25.7倍
10,000件 約58分(推定) 73.6秒 47.5倍

特に注目すべきは、バッチ検索の処理時間が1000件の62秒から1万件の73.6秒へとわずか12秒程度の増加にとどまっている点です。クエリ数が10倍になっても処理時間は約1.2倍にしかならないこのスケーリング特性が、CORTEX_SEARCH_BATCHの最大の強みです。一方、インタラクティブ検索は線形的に処理時間が増加するため、規模が大きくなるほど差が広がる構造が見て取れるでしょう。

インタラクティブ検索が毎秒約2.9件で頭打ちになるボトルネックの原因

インタラクティブ検索のスループットが毎秒約2.9件で頭打ちになる原因は、その設計思想と実行モデルに根ざしています。Python SDKやREST APIを使ったインタラクティブ検索では、各クエリが独立したHTTPリクエストとして送信されます。1件あたりの応答時間は約300〜350ミリ秒と高速ですが、逐次的にリクエストを送信するモデルでは、ネットワークラウンドトリップやリクエストのシリアライゼーションがボトルネックになってしまいがちでしょう。

さらに、Cortex Searchはインタラクティブワークロード向けに設計されたシステムであり、公式ドキュメントでは1サービスあたり20QPS、アカウント全体で140QPSを超えるとHTTP 429ステータスコードが返されスロットリングが発生すると明記されています。逐次実行では1件あたりの往復時間が律速となるため、実効スループットはそれよりもさらに低い約2.9件/秒にとどまってしまうのが現状となっています。

Python SDKを使った並行実行(concurrent queries)でスループットを改善するアプローチも存在しますが、スロットリング閾値の制約は残るため、数千件規模の処理には根本的に不向でしょう。CORTEX_SEARCH_BATCHがこの制約を解消できるのは、個別のHTTPリクエストではなく、Cortex Search Service側の内部パイプラインでクエリを並列処理する仕組みを採用しているためです。

バッチ検索が毎秒136件に到達するまでのスケーリング特性と再現条件

バッチ検索のスループットは、クエリ数の増加に応じて段階的に向上していくことになるでしょう。100件の処理では毎秒約2.9件とインタラクティブ検索と同等の水準ですが、1000件では毎秒約16件に到達し、1万件では毎秒約136件という圧倒的なスループットを記録しました。この特性は、起動オーバーヘッドの償却と並列処理の効率化が相乗効果を生み出す結果です。

このスケーリング特性を再現するための条件として、ベンチマークではSMALLサイズのウェアハウスと10万件のWikipedia記事データセットが使用されました。各クエリに対して5件の結果を返却する設定で、結果の取得サイズは1万件のクエリで最大5万行に達します。バッチ検索のスループットはウェアハウスサイズに依存しないため、より大きなウェアハウスを使用しても同等の結果が得られると考えてよいでしょう。

ただし、これらの数値は公式ガイドで「参考値であり、公式な性能指標ではない」と明記されている点に注意が必要です。インデックスのデータ量やクエリテキストの長さ、返却カラム数などの要因がスループットに影響を与えるため、自社環境での再現テストが導入判断の前提として推奨されます。複数のバッチクエリを並列実行すればさらに高いスループットが達成可能であるとも案内されており、数十万件規模の処理では分割並列実行の設計が有効となるでしょう。

SEARCH_PREVIEWやREST APIとの機能差で決めるクエリ方式の選定基準

Cortex Search Serviceに対するクエリ手段は、CORTEX_SEARCH_BATCHだけではありません。SEARCH_PREVIEW関数、REST API、Python SDKという3つのインタラクティブ検索手段が存在し、それぞれに異なる特性と適用範囲があります。バッチ検索を適切に活用するためには、他の方式との機能差を正確に理解し、ユースケースに応じた選定基準を持つことが不可欠となるでしょう。

SEARCH_PREVIEWが定数リテラル専用でバッチ不可となる技術的制約

SNOWFLAKE.CORTEX.SEARCH_PREVIEWは、Cortex Search Serviceの検索結果をSQLワークシート上で手軽に確認するためのスカラー関数です。クエリパラメータをJSON文字列として渡し、結果をJSON形式で受け取るシンプルなインターフェースを持っています。サービスの作成直後にインデックスが正しく機能しているかを検証する用途には最適な手段といえるでしょう。

しかし、SEARCH_PREVIEWには「定数リテラルしか受け付けない」という根本的な制約があります。テーブルのカラム値を動的に渡すことができないため、複数のクエリを実行するにはUNION ALLの羅列やストアドプロシージャによるループ処理が避けられません。加えて、結果サイズが300KBで切り捨てられるのに対し、REST APIでは最大10MBの結果を返却できるという差もあります。

Snowflakeの公式ドキュメントでも、SEARCH_PREVIEWは「テストおよび検証目的で設計されたものであり、低レイテンシが求められるエンドユーザーアプリケーションでの検索クエリ提供には使用しないでください」と明記されています。つまり、SEARCH_PREVIEWは開発・検証フェーズ限定のツールであり、バッチ処理はもちろん、本番のインタラクティブ検索にも推奨されない位置づでしょう。バッチ処理にはCORTEX_SEARCH_BATCH、本番のインタラクティブ検索にはREST APIまたはPython SDKを選択するのが適切な方式設計となるでしょう。

REST APIとPython SDKが低レイテンシ用途に適する応答速度300msの根拠

Cortex Search Serviceは作成時にREST APIエンドポイントを自動的にプロビジョニングします。エンドポイントのURL構造はhttps://<account_url>/api/v2/databases/<db>/schemas/<schema>/cortex-search-services/<service>:queryの形式で、PAT(プログラマティックアクセストークン)やJWT、OAuthによる認証が可能です。REST APIは1クエリあたり約300ミリ秒の応答速度を実現しており、チャットボットのRAGエンジンや検索バーのバックエンドとして十分な低レイテンシが提供されているのが特徴でしょう。

Python SDKはsnowflake.coreパッケージのバージョン0.8.0以降でCortex Search Serviceへのクエリをサポートしています。Rootオブジェクトを経由してサービスにアクセスし、searchメソッドでクエリを発行する形でしょう。内部的にはREST APIと同等のエンドポイントを呼び出しているため、応答速度も同等の水準になるのが通常です。Python SDKの利点は、Snowpark SessionやSnowflake Notebookとの統合が容易であり、データサイエンスワークフローの中にシームレスに組み込める点にあるでしょう。

REST APIとPython SDKはいずれも1件単位のクエリ処理に最適化されており、ユーザーの入力に対してリアルタイムで検索結果を返すインタラクティブ用途が主戦場です。バッチ的に使おうとすると逐次実行になり、スループットはSEARCH_PREVIEWと同じ壁にぶつかります。つまり、方式の選択は「1件の応答速度が重要か、大量処理のスループットが重要か」という1つの問いに集約されるでしょう。

リアルタイム検索とオフライン処理で方式を分けるユースケース判定フロー

クエリ方式の選定は、最終的にはユースケースの特性で決まります。判定フローの第一段階は「リアルタイムか、オフラインか」の二択です。ユーザーの入力に対して即座に結果を返す必要がある場合はインタラクティブ方式(REST APIまたはPython SDK)を選択し、事前に用意した大量のクエリを一括処理する場合はバッチ方式(CORTEX_SEARCH_BATCH)を選択するのが適切でしょう。

第二段階の判定基準はクエリ数の規模です。100件未満であれば方式による性能差はほぼなく、どちらを使っても問題ありません。100〜500件の範囲ではバッチ検索の方がやや速くなりますが、差は小さいため実装の容易さで選んでも問題ありません。500件を超えるとバッチ検索が明確に優位になり、1000件以上では常にバッチ検索を選択すべきです。

  • リアルタイム検索(チャットボット、検索バー):REST APIまたはPython SDK
  • 少量バッチ(100件未満):どちらでも同等の性能
  • 中規模バッチ(100〜1000件):CORTEX_SEARCH_BATCHが3〜6倍高速
  • 大規模バッチ(1000件以上):CORTEX_SEARCH_BATCHが必須、10〜47倍以上の高速化

この判定フローに加えて、将来的なクエリ数の増加を見据えた方式選択も重要です。現時点では500件程度でもインタラクティブ方式で運用可能ですが、データ量の増加に伴ってクエリ数が数千件規模に達することが予想される場合は、最初からバッチ方式で設計しておくほうが移行コストを抑えられるでしょう。

マルチインデックスクエリとバッチ検索を組み合わせる際の制約と注意点

Cortex Search Serviceは、単一のテキストカラムだけでなく、複数のテキストカラムやユーザー提供のベクトルカラムを組み合わせたマルチインデックス構成をサポートしています。CREATE CORTEX SEARCH SERVICEのTEXT INDEXESおよびVECTOR INDEXES句を使って定義するこの構成は、複数のフィールドを横断した高精度な検索を実現するために設計された機能にあたしょう。

マルチインデックスクエリではmulti_index_queryパラメータを使用して、どのインデックスを検索対象にするかを指定できます。この指定により、不要なインデックスの検索をスキップしてコストを削減できる利点があるのが実情でしょう。ただし、バッチ検索(CORTEX_SEARCH_BATCH)とマルチインデックスクエリの組み合わせについては、利用可能なパラメータや動作の制約を事前に公式ドキュメントで確認しておかなければなりません。

マルチインデックスクエリの特性として、テキストインデックスのみのクエリはエラーとなり、少なくとも1つのベクトルインデックスを含める必要がある点にも注意が求められます。また、テキストインデックスではOR検索(複数語句のいずれかにマッチ)、インデックス間ではAND検索(すべてのフィールドにマッチ)という動作になるため、バッチ検索で期待する結果を得るにはクエリ設計を慎重に行うことが不可欠でしょう。

リランカー無効化でレイテンシ100〜300ms削減が可能な品質トレードオフ

Cortex Searchの3段階パイプラインにおけるセマンティックリランキングは、検索品質を向上させる反面、レイテンシの増加要因にもなります。Snowflakeの公式情報では、リランキングの無効化によって1クエリあたり100〜300ミリ秒のレイテンシ削減が可能であると説明されています。インタラクティブ検索ではこの削減がユーザー体感に直結するため、品質よりも速度を優先するユースケースでは無効化を検討する価値があるでしょう。

リランキングの制御はscoring_configパラメータのrerankerフィールドで行い、"none"を指定することで無効化できます。この設定はクエリ単位で切り替えられるため、サービス全体の設定を変更する必要はありません。つまり、同一のCortex Search Serviceに対して、リランキング有効のクエリと無効のクエリを混在させることもできるようになっています。

バッチ検索においてリランキングを無効化する判断は、タスクの性質に依存します。エンティティ解決のように微妙な意味の違いを判別する必要があるタスクでは、リランキングの精度向上が結果品質に直結するため有効のまま運用するのが望ましいでしょう。一方、大まかなカテゴリ分けや初期スクリーニングが目的であれば、リランキングを無効化してバッチ全体の処理時間を短縮する選択も合理的です。品質と速度のトレードオフは、必ず実データでの比較評価を経てから判断すべきでしょう。

ウェアハウスサイズ非依存のコスト構造と本番運用で避けるべき設計上の落とし穴

Cortex Search Batchは高いスループットを提供する一方で、コスト構造を正確に把握していなければ想定外のクレジット消費につながるリスクが生じかねません。通常のSnowflakeワークロードとは異なるコスト発生メカニズムを持つため、既存の費用管理フレームワークをそのまま適用すると見誤る可能性があるのです。この章では、コストの内訳を分解したうえで、本番運用でよく発生する設計上の失敗パターンとその回避策を解説します。

仮想ウェアハウスからサービング課金まで公式5カテゴリで把握するコスト内訳

Cortex Search Serviceに関連するコストは、公式ドキュメントでは5つのカテゴリとして整理されているのが特徴でしょう。第1はウェアハウスコンピュートで、ソースクエリの実行やインデックス構築・リフレッシュに仮想ウェアハウスが消費するクレジットにあたります。第2はエンベディングトークンコンピュートで、検索対象カラムの各行をベクトル化する際にモデルごとの100万トークン単価に応じて課金される仕組みでしょう。第3はサービングコンピュートで、インデックスデータの非圧縮GB/月あたりの単価で継続的に課金され、クエリの有無に関わらず発生する点が特に重要な注意事項でしょう。第4はストレージコストで、マテリアライズされたテーブルと中間データ構造のTB単価課金にあたります。第5はクラウドサービスコンピュートで、ベースオブジェクトの変更検出に使われるものです。

バッチ検索に固有の考慮点として、ウェアハウスのクレジット消費はクエリテーブルの読み込みに限定され、検索処理そのものはCortex Search Service側の専用コンピュートで実行されるため、ウェアハウスコストは最小限に抑えられるでしょう。サービングコンピュートは「サービスがクエリに応答可能な状態にある限り」課金が継続するため、使用していない期間でもコストが発生する仕組みとなっています。つまり、バッチ検索のコスト主体はウェアハウスではなくAIサービスのクレジットであり、Snowflakeのコンソールで「AI Services」カテゴリとして計上される費用を重点的に監視しなければなりません。

日次のコスト詳細はCORTEX_SEARCH_DAILY_USAGE_HISTORYビューで確認できます。このビューにはアカウント内の各Cortex Search Serviceごとの日次消費量が集計されており、コスト増加のトレンドを早期に検出するためのモニタリング基盤として活用可能です。バッチ検索の実行頻度とクエリ数を記録し、コストとの相関を定期的に分析する運用が、予算超過を防ぐ有効な手段となるでしょう。

ウェアハウスを大きくしてもバッチ速度が変わらないリソース分離の設計意図

CORTEX_SEARCH_BATCHのスループットがウェアハウスサイズに依存しないという特性は、コスト最適化の観点で非常に重要な設計ポイントです。通常のSnowflakeワークロードでは、ウェアハウスを大きくするほど処理が速くなるという関係が成立するため、「バッチ検索を高速化するためにウェアハウスをスケールアップする」という発想に陥りがちですが、これはコストの無駄遣いにつながる典型的な誤りとなってしまいがちでしょう。

リソース分離の設計意図は、バッチ検索のスケーラビリティをウェアハウスの制約から切り離すことにあります。バッチ検索の並列処理はCortex Search Service内部の専用リソースで行われるため、ウェアハウスのサイズを上げても下げてもスループットに影響しません。この設計のおかげで、バッチ検索の実行にはSMALLウェアハウスを使い、コストを最小限に抑えられるのが利点となっています。

ただし、ウェアハウスサイズが影響を与える工程が2つ存在します。1つ目はCortex Search Serviceの初回構築時で、ソースクエリのマテリアライズ処理にウェアハウスが使われるでしょう。2つ目はリフレッシュ時で、ソースデータの変更を検出してインデックスを更新する工程にもウェアハウスのコンピュートが消費されます。これらの工程では、大規模データセットに対してMEDIUMサイズまでのウェアハウスを使うことが公式に推奨されているのが特徴でしょう。バッチ検索の実行とサービスの構築・更新で別のウェアハウスを使い分ける設計が、コスト効率の最適解となるでしょう。

ソーステーブル1億行上限とインデックス肥大化で起きる性能劣化の回避策

Cortex Search Serviceには、マテリアライズされたクエリ結果が1億行を超えるとサービス作成がエラーになるという上限が設定されています。この制約は、検索インデックスのサイズが最適なサービング性能を維持できる範囲に収まるよう設計されたガードレールにあたります。1億行を超えるデータセットに対してCortex Searchを適用したい場合は、Snowflakeのアカウントチームに連絡して上限の引き上げを相談する必要があるのが実情でしょう。

1億行未満であっても、インデックスサイズが大きくなるとバッチ検索のスループットに影響が出る可能性があります。公式ドキュメントでは「バッチ検索のスループットはインデックスに登録されたデータ量によって変動する」と明記されており、データ量が増えるほど1クエリあたりの検索処理に要する計算量も増加する傾向にあるためにほかなりません。定期的にインデックスサイズの推移を監視し、不要なデータの除外やパーティショニングの導入を検討することが性能維持に役立ちます。

ソースクエリのAS句でWHERE条件を指定してインデックス対象を絞り込む手法も効果でしょう。たとえば、直近1年分のデータのみをインデックス化するフィルタを設定すれば、インデックスサイズの肥大化を抑制しつつ、バッチ検索の実用的な範囲をカバーできます。データのライフサイクル管理とインデックスの範囲設計を連動させることが、長期運用における性能劣化の回避策として有でしょう。

TARGET_LAGを短くしすぎた場合に発生するリフレッシュコスト増大の失敗例

TARGET_LAGの設定は、インデックスの鮮度とリフレッシュコストのトレードオフを直接的に制御するパラメータです。リアルタイム性を重視して'1 minute'のように極端に短い値を設定すると、ソーステーブルの微小な変更に対しても頻繁にリフレッシュが実行され、AIサービスのコンピュートクレジットが想定以上に消費されるリスクがあるのが実情でしょう。

バッチ検索のユースケースでは、多くの場合TARGET_LAGを'1 hour'から'1 day'の範囲で設定するのが合理的です。夜間バッチの名寄せジョブであれば、前日のデータが反映されていれば十分であり、'1 day'の設定でリフレッシュ頻度を最小限に抑えられるでしょう。一方、データの更新頻度が高く、バッチ検索の結果に最新データの反映が求められる場合でも、'1 hour'程度あれば実務上十分なケースがほとんどでしょう。

TARGET_LAGの設定を間違えた場合の影響は、コスト面だけにとどまりません。リフレッシュ処理が高頻度で実行されると、ウェアハウスの稼働時間が延び、AUTO_SUSPENDによる停止が機能しにくくなります。また、ソーステーブルのデータ保持期間(DATA_RETENTION_TIME_IN_DAYS)よりもTARGET_LAGが長い場合、変更検出が正しく機能しなくなりサービスの再作成が必要になるというリスクも存在します。初期設定では保守的に長めの値を設定し、運用データを蓄積しながら最適値に調整するアプローチが推奨されるでしょう。

5回連続リフレッシュ失敗で自動サスペンドされる障害対応と復旧手順

Cortex Search Serviceは、ソースクエリの実行に関連するエラーが5回連続で発生すると、インデクシング状態が自動的にSUSPENDEDに移行します。この挙動はDynamic Tablesと同様のフェイルセーフ機構であり、ソースデータの問題が継続的にリソースを消費し続けることを防ぐ目的で設計されています。サスペンド状態になると、インデックスの更新が停止し、バッチ検索の結果は最後に成功したリフレッシュ時点のデータに固定されるでしょう。

障害の原因を特定するには、DESCRIBE CORTEX SEARCH SERVICEコマンドまたはCORTEX_SEARCH_SERVICESビューを参照します。出力に含まれるINDEXING_STATEカラムでSUSPENDED状態を確認でき、INDEXING_ERRORカラムにはソースクエリで発生した具体的なSQLエラーが記録されているのが特徴でしょう。よくある原因としては、ソーステーブルの削除や名前変更、カラムの型変更、権限の取り消しなどが挙げられるでしょう。

根本原因を解決した後は、ALTER CORTEX SEARCH SERVICE <service_name> RESUME INDEXING;コマンドでインデクシングを再開できます。再開後、サービスは次回のリフレッシュサイクルで最新のソースデータを取り込み、インデックスの更新を再開します。本番環境では、INDEXING_STATEの監視をアラートシステムに組み込み、SUSPENDED状態を即時検知できる体制を構築しておくことが安定運用の鍵となるでしょう。リフレッシュエラーの発生頻度が高い場合は、ソースクエリの複雑度を見直すか、より安定したビューやマテリアライズドビューをソースとして使用する方法も検討に値するでしょう。

SQLとPython SDK両対応で実装するバッチ検索の実践パターンと応用例

ここまでの章でCortex Search Batchの設計思想、性能特性、コスト構造を一通り把握しました。最後の章では、実際のコード実装に焦点を当て、SQLとPython SDKそれぞれの実装パターンを提示します。さらに、Cortex LLM Functionsとの連携やStreamlitダッシュボードとの統合といった応用構成についても具体的に解説し、バッチ検索をデータパイプラインの中核として活用するための設計指針を示します。

SQLのみで完結する1000件バッチ検索の最小構成テンプレートと実行例

最もシンプルなバッチ検索の実装は、SQL文だけで完結するパターでしょう。外部ライブラリやSDKの導入が不要で、Snowsightのワークシートから即座に実行できるため、検証段階での採用に適した手段といえるでしょう。以下は、既存テーブルからランダムに1000件のクエリを抽出してバッチ検索を実行し、結果を永続テーブルに保存する最小構成のテンプレートです。

  1. 検索クエリ用テーブルの作成:CREATE OR REPLACE TABLE search_queries AS SELECT DISTINCT title AS query_text FROM source_table ORDER BY RANDOM() LIMIT 1000;
  2. バッチ検索の実行と結果の保存:CREATE OR REPLACE TABLE batch_results AS SELECT q.query_text, s.* FROM search_queries AS q, LATERAL CORTEX_SEARCH_BATCH(service_name => 'DB.SCHEMA.MY_SERVICE', query => q.query_text, limit => 5) AS s;
  3. 結果の件数確認:SELECT COUNT(*) FROM batch_results;

この3ステップで、1000件のクエリに対する最大5000件の検索結果がbatch_resultsテーブルに格納されます。CREATE TABLE AS SELECT形式を使うことで、バッチ検索の実行と結果の永続化を1つのステートメントで完了できるのがポインでしょう。結果テーブルには検索元のクエリテキストと、Cortex Search Serviceから返却された各カラムの値が含まれるため、後続のJOINや集計処理にそのまま活用できる点が魅力となっています。

この最小構成をSnowflakeのタスクスケジューラと組み合わせれば、定期的なバッチ検索ジョブとして自動化することも容でしょう。たとえば、毎晩新規に追加された顧客データに対する名寄せバッチを実行し、翌朝にはレビュー対象リストが準備されているという運用フローを構築できます。

Python SDKでインタラクティブとバッチを切り替える分岐実装のコード例

Python SDKを使った実装では、クエリ数に応じてインタラクティブ検索とバッチ検索を動的に切り替えるロジックを組み込むことが可でしょう。Snowflake Notebookやローカルのスクリプトから、snowflake.coreパッケージのRootオブジェクト経由でCortex Search Serviceにアクセスし、少量のクエリにはsearchメソッド、大量のクエリにはSQLベースのバッチ検索を使い分ける設計が可能となっています。

分岐の閾値としては、ベンチマーク結果に基づいて100件を境界とするのが実用でしょう。100件未満であればPython SDKのsearchメソッドをforループで呼び出し、100件以上であればクエリテーブルを動的に作成してCORTEX_SEARCH_BATCHを実行するSQLをsession.sqlで発行します。この分岐ロジックをヘルパー関数として定義しておけば、呼び出し側はクエリリストを渡すだけで、最適な方式が自動的に選択される設計が実現するでしょう。

Python SDKでのインタラクティブ検索のコードは、サービスオブジェクトのsearchメソッドにquerycolumnslimitを渡すシンプルな形式です。一方、バッチ検索のSQL発行時にはクエリテキストのエスケープ処理が必要になります。シングルクォートを含むクエリテキストをINSERT文に埋め込む場合、chr(39)のダブルエスケープ処理を行うことでSQLインジェクションを防止し、安全にバッチ検索を実行できるようになっているのが実情でしょう。

500件チャンク分割でSQL長制限を回避する大量INSERT時の実装テクニック

バッチ検索のクエリテーブルにデータを投入する際、数千件以上の値をINSERT文で一度に送信しようとすると、SQLのサイズ制限に抵触する場合があります。特にクエリテキストが長い場合やUnicode文字を多く含む場合は、SQL文字列の総バイト数が上限を超えやすくなしょう。この問題を回避するために、チャンク分割によるINSERTが実用的なテクニックとして推奨されます。

具体的には、クエリリスト全体を500件ずつのチャンクに分割し、各チャンクごとにINSERT文を発行する方でしょう。Python実装では、リストのスライスを使ってsearch_queries[i:i+chunk_size]のように分割し、各チャンクのVALUES句を組み立ててsession.sql(f"INSERT INTO temp_queries VALUES {values}").collect()で実行します。チャンクサイズの500件は公式ガイドでも使用されている値であり、多くの環境で安定した動作が見込めるでしょう。

チャンク分割のもう一つの利点は、進捗の可視化が容易になる点です。各チャンクのINSERT完了後にカウンターを更新して進捗率を表示するロジックを組み込めば、数万件のクエリテーブル作成プロセスを途中経過つきで監視できます。CORTEX_SEARCH_BATCH自体の実行はクエリテーブルの準備が完了した後の1回のSELECTで済むため、全体の処理時間におけるINSERT工程の割合はわずかですが、大規模運用ではこうしたロバスト性を確保するテクニックが安定稼働の土台となっていくでしょう。

Cortex LLM Functionsと連携してRAG分類精度を51倍改善する応用構成

CORTEX_SEARCH_BATCHの応用領域を大きく広げるのが、Cortex LLM Functions(AI_COMPLETEなど)との連携パイプラインとなっています。バッチ検索とLLMを組み合わせることで、大規模テキスト分類のコスト効率を劇的に改善できることが実証されました。代表的な構成は、バッチ検索で候補カテゴリを絞り込み、絞り込まれた候補のみをLLMのプロンプトに含めて最終分類を実行する2段階パイプラインです。

この手法の効果を示す具体的な事例として、1000カテゴリに対する100万SKUの分類タスクが挙げられます。LLM単体ですべてのカテゴリを含むプロンプトを送信すると、トークン数が膨大になりコストが急増するのが一般的でしょう。そこで、まずCortex Searchでカテゴリ定義文をインデックス化し、各SKUの商品説明をバッチ検索にかけて上位数件の候補カテゴリを取得します。その候補のみをAI_COMPLETEのプロンプトに含めて最終分類を行えば、LLMに送信するコンテキスト量が大幅に削減され、分類コストが最大で51倍削減できたという結果が報告されているのが特徴でしょう。

この構成はバッチ検索の高スループットとLLMの高精度な判断能力の長所を組み合わせたものであり、単独では実現できないコストパフォーマンスを達成します。バッチ検索が「候補の絞り込み」というフィルタリング層として機能し、LLMへの入力を最適化する役割を果たすという設計パターンは、分類タスクに限らず、要約・抽出・判定など幅広いAIワークフローに応用できる余地があるでしょう。

Streamlitダッシュボードからバッチ結果を可視化する運用モニタリング設計

バッチ検索の結果を運用チームが継続的に監視・分析するためには、可視化基盤の構築が重要です。SnowflakeにはStreamlit in Snowflakeが組み込まれており、Pythonコードからインタラクティブなダッシュボードを構築できます。バッチ検索の結果テーブルを直接参照するStreamlitアプリを作成すれば、名寄せ候補のレビュー画面や重複検出のサマリーダッシュボードをSnowflake環境内で完結させることが可能となっているのが実情でしょう。

モニタリングダッシュボードに含めるべき主要な指標は、バッチ検索の実行時間、処理クエリ数、結果の類似スコア分布、マッチ率の推移です。たとえば、エンティティ解決ジョブの結果における類似スコアの分布をヒストグラムで表示すれば、自動マージ閾値の妥当性を視覚的に検証でくことになるでしょう。スコアが二峰性の分布を示す場合は閾値が適切に機能している証拠であり、連続的な分布を示す場合は閾値の再検討が必要なシグナルとして捉えることができるでしょう。

Cortex Search Serviceの健全性監視もダッシュボードに統合する価値があります。SHOW CORTEX SEARCH SERVICESDESCRIBE CORTEX SEARCH SERVICEの出力から、INDEXING_STATEやINDEXING_ERRORの情報を取得し、サービスの稼働状態をリアルタイムに表示するパネルを設ければ、リフレッシュ失敗の早期検知が可能です。CORTEX_SEARCH_DAILY_USAGE_HISTORYビューのデータをトレンドチャートとして表示することで、コスト推移の異常値も即座に把握できる体制が整いるのが現状でしょう。バッチ検索を単発の実行で終わらせず、継続的なデータ品質管理の仕組みとして定着させるためには、こうした運用基盤の整備が不可欠です。

資料請求

RELATED POSTS 関連記事