AI検索エンドポイントの負荷テストを構成します
このページでは、AI Searchエンドポイントの負荷テストに関するガイダンス、サンプルコード、およびサンプルノートブックを提供しています。負荷テストは、AI Search エンドポイントが本番環境にデプロイされる前に、そのパフォーマンスと本番運用の準備状況の確認に役立ちます。負荷テストからわかること:
- 異なるスケーリングレベルでのレイテンシ
- スループットの制限とボトルネック(1 秒あたりのリクエスト数、レイテンシの内訳)
- 持続的な負荷時のエラー率
- リソース使用率とキャパシティ プランニング
ロード テストと関連する概念の詳細については、サービス エンドポイントのロード テストを参照してください。
要件
これらのステップを開始する前に、デプロイ済みのAI検索エンドポイントと、そのエンドポイントに対し、 Can Query 権限を持つサービスプリンシパルが必要です。「ステップ 1: サービスプリンシパルの認証を設定する」を参照してください。
以下のファイルと例のノートブックをダウンロードして、Databricks ワークスペースにインポートしてください。
- input.json。これは、すべての並列接続によってエンドポイントに送信されるペイロードを指定する
input.jsonファイルの例です。必要に応じて複数のファイルを持つことができます。サンプルノートブックを使用する場合、このファイルは提供された入力テーブルから自動的に生成されます。 - fast_vs_load_test_async_load.py。このスクリプトをワークスペースにアップロードし(例:
/Workspace/Users/<your-username>/fast_vs_load_test_async_load.py)、locust_script_pathノートブック・パラメーターをそのパスに設定します。このスクリプトは、認証、ペイロードの配信、およびデバッグメトリクスの収集を処理します。 - 次の サンプルノートブック は、負荷テストを実行します。最高のパフォーマンスを得るには、このノートブックを、多数のコアを備えたSingle Node クラスターで実行してください (Locust は利用可能なすべての CPU にわたってスケーリングします)。事前生成された埋め込みを使用するクエリには、高メモリが推奨されます。
ノートブックの例およびクイックスタート
以下の例のノートブックを使用して開始してください。2つの探索モードをサポートしています。それは、定義する特定の同時実行レベルをテストする段階的なスイープと、数ステップで最大の持続可能なQPS(ブレークポイント)を自動的に検出するバイナリ検索モードです。すべてのパラメーターはウィジェットを使用して構成されるため、ノートブックはコードを編集することなく、インタラクティブに、または Databricks ジョブとして実行できます。
Locust load test ノートブック
負荷テスト フレームワーク:Locust
Locustは、オープンソースの負荷テスト フレームワークで、次のことができます。
- 並列クライアント接続の数を調整します。
- 接続の生成速度を制御します
- テスト全体を通じてエンドポイントのパフォーマンスを測定します。
- 利用可能なすべてのCPUコアを自動で検出して使用します。
サンプルのノートブックは、「--processes -1」フラグを使用してCPUコアを自動検出し、それらを最大限に活用します。
Locust が CPU によってボトルネックになっている場合、出力にメッセージが表示されます。
ステップ 1: サービスプリンシパル認証を設定する
本番運用に近いパフォーマンステストには、常にOAuthサービスプリンシパル認証を使用してください。サービスプリンシパルは、個人用アクセストークン (PAT) に比べて、応答時間を最大100ミリ秒高速化し、要求レート制限を向上させます。
サービスプリンシパルを作成および構成
-
Databricks サービスプリンシパルの作成手順については、サービスプリンシパルをアカウントに追加するを参照してください。
-
権限を付与:
- AI検索エンドポイントページに移動してください。
- [ アクセス許可 ]をクリックします。
- サービスプリンシパルに Can Query 権限を与えます。
-
OAuthシークレットを作成してください。
- サービスプリンシパルの詳細ページに移動します。
- [ シークレット ] タブをクリックします。
- [ シークレットを生成 ] をクリックします。
- 有効期間を設定します (長期テストには365日を推奨します)。
- 「 クライアントID 」と「 シークレット 」の両方を直ちにコピーしてください。
-
認証情報を安全に保管してください。
- Databricks シークレットスコープを作成する。手順については、「チュートリアル: Databricks シークレットを作成して使用する」を参照してください。
- 以下のコード例に示すように、サービスプリンシパルのクライアント ID を
service_principal_client_idとして保存し、OAuth シークレットをservice_principal_client_secretとして保存します。
Python# In a Databricks notebook
dbutils.secrets.put("load-test-auth", "service_principal_client_id", "<CLIENT_ID>")
dbutils.secrets.put("load-test-auth", "service_principal_client_secret", "<SECRET>")
ステップ2:ロードテストを設定する
ノートブックの構成
ノートブックの上部にあるウィジェットを使用して、ノートブックのパラメーターを構成します。Databricksジョブとしてノートブックを実行する際は、これらの値をジョブパラメーターとして渡してください。コードの編集は必要ありません。
パラメーター | 説明 | 推奨値 |
|---|---|---|
| AI検索エンドポイントの名前 | エンドポイント名 |
| 完全なインデックス名( | インデックス名 |
| クエリをサンプリングするためのソーステーブル ( | インデックス入力テーブル |
| 管理埋め込みに使用するテキスト列 |
|
| 事前計算されたエンベディングベクトルを含む列。セルフマネージド型埋め込みにのみ使用されます。 | 管理対象の埋め込みについては空白のままにします。 |
| テスト用にサンプリングするクエリーの数 |
|
| テストする並列クライアント数のカンマ区切りリスト |
|
| 同時実行レベルごとの秒数。1つの値がすべてのレベルに適用されます。または、レベルごとに1つをカンマ区切りリストとして指定します。 |
|
| Databricks シークレットスコープ名 | スコープ名 |
|
|
|
| (オプション)結果を保存する Delta テーブル( |
|
| 後で分析できるように、この実行をタグ付けする名前またはコメント | 説明ラベル |
|
|
|
| ( |
|
| ( |
|
| ( |
|
| クエリーごとに返される結果の数 |
|
| クエリ結果で返される列のコンマ区切りリスト(例えば、 | デフォルトを使用する場合は、空白のままにしてください。 |
マネージド型とセルフマネージド型の埋め込み
ノートブックは、マネージド埋め込み(Databricksがクエリ時に埋め込みを生成します)とセルフマネージド埋め込み(事前計算されたベクトルを直接渡します)の両方に対応しています。インデックスタイプに基づいて、適切なパラメーターを構成してください。
インデックスタイプ | 設定するパラメーター | 未設定のままにしてください |
|---|---|---|
マネージド型埋め込み (Databricksによる埋め込みモデルを備えたDelta Syncインデックス) |
|
|
セルフマネージド型埋め込み (事前計算済みベクトルを備えたDelta SyncまたはDirect Vector Accessインデックス) |
|
|
マネージド埋め込みインデックスの場合、ロードテストは埋め込み生成時間を含むエンドツーエンドのレイテンシを測定します。エンべディングエンドポイントがゼロにスケーリングする場合、最初のテスト実行でコールドスタートのオーバーヘッドが発生します。検索遅延から埋め込み遅延を切り離す方法については、埋め込みモデルのボトルネックを特定するを参照してください。
5~10分かかるのはなぜですか?
5分の最小テスト期間は不可欠です。
- 初期クエリにはコールドスタートオーバーヘッドが含まれる場合があります。
- エンドポイントが安定したパフォーマンスに達するには時間がかかります。
- モデルサービングのエンドポイントのオートスケーリング(有効化されている場合)は、アクティブ化に時間がかかります。
- 短時間のテストでは、継続的な負荷がかかっている状況でのスロットリング動作を把握できません。
次の表では、テストの目標に応じた推奨テスト期間を示しています。
テストタイプ | テスト期間 | テストの目標 |
|---|---|---|
クイックスモークテスト | 2~3分 | 基本的な機能を確認する |
パフォーマンス ベースライン | 5~10分 | 信頼性の高い定常状態メトリクス |
ストレステスト | 15~30分 | リソース枯渇の特定 |
耐久テスト | 1~4時間 | 劣化、レイテンシーの安定性 |
ブレークポイント探索(バイナリ検索モード)
グラジュアルスイープ (exploration_mode=gradual) に加えて、ノートブックは、同時実行レベルを手動で指定することなく最大持続可能QPSを特定する、自動バイナリサーチモードをサポートしています。
仕組み
exploration_mode=binary_searchを設定し、max_target_qpsを指定します(例:500)。このノートブックは、リトルの法則(concurrency = QPS × avg_latency_sec)を使用して各QPSターゲットを推定される同時実行レベルに変換し、次に、以下のように二分探索を実行します。
max_target_qps / 2から開始します(例の場合250)。- エラー率が
error_rate_thresholdを下回る場合(成功)、下限を引き上げ、より高いQPS(375、その後500など)を試してください。 - エラー率が閾値(失敗)を超えた場合は、上限を下げ、最後の成功と失敗の中間点を試してください。
- 最大
exploration_stepsステップ(デフォルト8)繰り返すか、検索範囲がmax_target_qpsの5%以内に狭まるまで繰り返します。
次の表は、約430 QPS付近に限界点がある仮想のエンドポイントにおける検索の収束状況を示しています。
ステップ | ターゲットQPS | エラー率 | 結果 | 新規範囲 |
|---|---|---|---|---|
1 | 250 | 0.1% | 成功 | [250、500] |
2 | 375 | 0.3% | 成功 | [375, 500] |
3 | 437 | 4.5% | 失敗 | [375, 437] |
4 | 406 | 0.8% | 成功 | [406, 437] |
5 | 421 | 2.1% | 失敗 | [406, 421] |
5~8ステップで、検索はブレークポイント(この例では約406~421 QPS)に収束します。これは、網羅的なスイープよりもはるかに少ないテスト実行回数で実現されます。
各モードの使い分け
モード | 使用する場合: |
|---|---|
| すでに予想される動作範囲がわかっており、特定の同時実行レベルでパフォーマンスの特性を評価したいです。 |
| 事前に同時実行レベルを把握していなくても、迅速に持続可能な最大QPSを特定したい。 |
ステップ 3: クエリセットを設計する
クエリセットは、予想される本番運用トラフィックを可能な限り忠実に反映する必要があります。具体的には、コンテンツ、複雑さ、多様性の観点から、クエリの期待される分布に合わせるようにしてください。
-
現実的なクエリを使用する。「test query 1234」のようなランダムなテキストは使用しないでください。
-
期待される本番運用のトラフィック分布に一致させます。80%の一般的なクエリ、15%の中程度の頻度のクエリ、そして5%の低頻度のクエリを想定している場合、クエリセットはその分布を反映している必要があります。
-
本番運用で想定されるクエリのタイプに合わせてください。例えば、本番運用のクエリでハイブリッド検索またはフィルターを使用することを想定している場合は、クエリセットでもそれらを使用する必要があります。
フィルターを使用するクエリーの例:
JSON{
"query_text": "wireless headphones",
"num_results": 10,
"filters": { "brand": "Sony", "noise_canceling": true }
}ハイブリッド検索を使用したクエリーの例:
JSON{
"query_text": "best noise canceling headphones for travel",
"query_type": "hybrid",
"num_results": 10
}
クエリの多様性とキャッシュ
AI検索エンドポイントは、パフォーマンスを向上させるために数種類のクエリ結果をキャッシュします。このキャッシュは負荷テストの結果に影響を与える可能性があります。このため、クエリ セットの多様性に注意することが重要です。たとえば、同じクエリ セットを繰り返し送信する場合は、実際の検索パフォーマンスではなく、キャッシュをテストしていることになります。
使用: | 時期: | 例 |
|---|---|---|
同一あるいは少ないクエリ |
| 「トレンドアイテム」を表示する製品レコメンデーションウィジェット — 同じクエリは1時間あたり数千回実行されます。 |
多様なクエリ |
| 各ユーザーが異なる製品検索を入力するeコマース検索。 |
追加の推奨事項については、「ベストプラクティスの概要」を参照してください。
クエリーセット作成オプション
コードタブには、多様なクエリセットを作成するための3つのオプションが表示されます。全てに当てはまるものはありません。ご自身にとって最適なものを選択してください。
- (推奨) インデックス入力テーブルからのランダムサンプリングこれは良い出発点です。
- 本番運用ログからのサンプリング。本番運用ログがあれば、これは良いスタートです。クエリは通常、時間の経過とともに変化します。そのため、テストセットを定期的に更新して、最新の状態に保ってください。
- 合成クエリ生成中です。これは、本番運用ログがない場合や、複雑なフィルターを使用している場合に便利です。
- Random sampling from input table
- Sample from production logs
- Synthetic queries
次のコードは、インデックス入力テーブルからランダムなクエリを取得します。
import pandas as pd
import random
# Read the index input table
input_table = spark.table("catalog.schema.index_input_table").toPandas()
# Sample random rows
n_samples = 1000
if len(input_table) < n_samples:
print(f"Warning: Only {len(input_table)} rows available, using all")
sample_queries = input_table
else:
sample_queries = input_table.sample(n=n_samples, random_state=42)
# Extract the text column (adjust column name as needed)
queries = sample_queries['text_column'].tolist()
# Create query payloads
query_payloads = [{"query_text": q, "num_results": 10} for q in queries]
# Save to input.json
pd.DataFrame(query_payloads).to_json("input.json", orient="records", lines=True)
print(f"Created {len(query_payloads)} diverse queries from index input table")
次のコードサンプルは、本番運用クエリから比例的に抽出されています。
# Sample proportionally from production queries
production_queries = pd.read_csv("queries.csv")
# Take stratified sample maintaining frequency distribution
def create_test_set(df, n_queries=1000):
# Group by frequency buckets
df['frequency'] = df.groupby('query_text')['query_text'].transform('count')
# Stratified sample
high_freq = df[df['frequency'] > 100].sample(n=200) # 20%
med_freq = df[df['frequency'].between(10, 100)].sample(n=300) # 30%
low_freq = df[df['frequency'] < 10].sample(n=500) # 50%
return pd.concat([high_freq, med_freq, low_freq])
test_queries = create_test_set(production_queries)
test_queries.to_json("input.json", orient="records", lines=True)
まだ本番運用のログがない場合、多様な合成クエリを生成できます。
# Generate diverse queries programmatically
import random
# Define query templates and variations
templates = [
"find {product} under ${price}",
"best {product} for {use_case}",
"{adjective} {product} recommendations",
"compare {product1} and {product2}",
]
products = ["laptop", "headphones", "monitor", "keyboard", "mouse", "webcam", "speaker"]
prices = ["500", "1000", "1500", "2000"]
use_cases = ["gaming", "work", "travel", "home office", "students"]
adjectives = ["affordable", "premium", "budget", "professional", "portable"]
diverse_queries = []
for _ in range(1000):
template = random.choice(templates)
query = template.format(
product=random.choice(products),
product1=random.choice(products),
product2=random.choice(products),
price=random.choice(prices),
use_case=random.choice(use_cases),
adjective=random.choice(adjectives)
)
diverse_queries.append(query)
print(f"Generated {len(set(diverse_queries))} unique queries")
ステップ4.ペイロードをテスト
完全なロードテストを実行する前に、ペイロードを検証してください。
- Databricks ワークスペースで、AI検索エンドポイントに移動してください。
- 左側のサイドバーで、**サービング**をクリックしてください。
- エンドポイントを選択。
- 利用 > クエリー をクリックします。
input.jsonの内容をクエリボックスに貼り付けます。- エンドポイントが期待どおりの結果を返すことを確認してください。
これにより、ロードテストではエラー応答ではなく、現実的なクエリが測定されます。
ステップ 5: ロードテストを実行する
接続チェックとウォームアップ
ロードテストを開始する前に、ノートブックは2つのセットアップステップを実行します。
-
接続チェック :サービスプリンシパルの資格情報を使用して、単一のプローブクエリを送信します。エンドポイントが401または403エラーを返した場合、ノートブックは、エラーデータのみを生成する完全な負荷テストを実行する代わりに、明確な
PermissionErrorで直ちに失敗します。資格情報や権限の設定が誤っている場合に、時間を節約できます。 -
ウォームアップテスト(1分間): エンドポイントキャッシュをウォームアップし、エンドツーエンドのリクエストフローを検証する短い低並行性テストを実行します。ウォームアップの結果はパフォーマンスメトリクスには使用されません。バイナリ検索モードでは、ウォームアップレイテンシーはリトルの法則に基づく同時実行性推定のベースラインとしても使用されます。
メイン負荷テスト シリーズ
ノートブックは、クライアントの同時実行数を段階的に増やしながら一連のテストを実行します。
- 開始: 低並列 (例: 5 並列クライアント)
- 中: 中程度のコンカレンシー(例えば、10、20、または 50 クライアント)
- 終了: high concurrency (例: 100 クライアント以上)
各テストは、step_duration_secondsで設定された期間実行されます(5~10分を推奨します)。
ノートブックが測定する内容とは
このノートブックでは、以下の項目を測定し、報告します:
レイテンシー メトリクス:
- P50(中央値): クエリの半分はこれよりも高速です。
- P95: クエリの95%がこれより高速です。これは主要なSLAメトリクスです。
- P99: クエリの99%がこの値よりも高速です。
- 最大: 最悪の場合のレイテンシ。
スループット メトリクス:
- RPS(1秒あたりのリクエスト): 1秒あたりの成功したクエリーです。
- 総クエリー数: 完了したクエリー数。
- 成功率: 成功したクエリの割合です。
エラー:
- タイプ別のクエリー失敗
- 例外メッセージ
- タイムアウト回数
結果の保存
output_tableパラメーターが設定されている場合、ノートブックは同時実行レベルごと (またはバイナリサーチのステップごと) に1行をUnity Catalog Deltaテーブルに保存します。テーブルは、最初の実行時に自動的に作成され、その後の実行で追加されます。各行には、run_name、exploration_mode、同時実行性、成功/失敗率、レイテンシーパーセンタイル、RPS、およびバイナリー検索固有のフィールド(bs_step、bs_target_qps、bs_outcome)が含まれます。SQLまたはBIツールを使用すると、実行を時系列で比較できます。
Databricksジョブとして実行中
すべてのノートブックパラメーターは dbutils.widgets として定義されており、Databricksジョブパラメーターに直接マッピングされます。負荷テストをスケジュールまたは自動化するには:
- ノートブックをタスクとしてジョブを作成します。
- ウィジェットの値をジョブパラメーターとして設定します。コードの編集は必要ありません。
- 多数のCPUコアを持つシングルノード クラスターにジョブをアタッチします(Locustは並列ワーカーから恩恵を受けます)。
- オンデマンドで、または定期的なベースラインテスト用にスケジュールで実行します。
ステップ6:結果を解釈する。
次の表は、良好なパフォーマンスのターゲットを示しています。
メトリクス | ターゲット | Comment |
|---|---|---|
P95 レイテンシー | 500ms 未満 | ほとんどのクエリは高速です。 |
P99 レイテンシー | 1秒未満 | ロングテールクエリにおける妥当なパフォーマンス |
成功率 | >99.5% | 低い失敗率 |
時間の経過に伴う待機時間 | 安定 | テスト中に低下は確認されませんでした。 |
1 秒あたりのクエリー | 目標を達成しています | エンドポイントは想定されるトラフィックを処理できます |
以下の結果はパフォーマンスの低下を示しています。
- P95 > 1秒。クエリがリアルタイムでの使用には遅すぎます。
- P99 > 3秒。ロングテールクエリのレイテンシーは、ユーザー体験を損ないます。
- 成功率 <99%。失敗が多すぎます。
- レイテンシーが増加しています。リソースの枯渇またはメモリ リークを示します。
- レート制限エラー(429)。より多くのエンドポイント容量が必要です。
RPSとレイテンシのトレードオフ
最大RPSは、本番運用スループットにとって最適な点ではありません。レイテンシーは、最大スループットに近づくにつれて非線形的に増加します。最大RPSで稼働する場合、最大容量の60~70%で稼働する場合と比較して、多くの場合、レイテンシーが2~5倍高くなることがよくあります。
次に、最適な動作点を見つけるために結果を分析する方法を示します。
- 150の並列クライアントの場合、最大RPSは480です。
- 最適な稼働点は、50 並列クライアントで 310 RPS(65% の能力)です。
- 最大でのレイテンシーのペナルティ:P95 は 4.3 倍高くなっています(1.5秒 対 350ミリ秒)
- この例では、エンドポイントを480 RPSの容量に設定し、約310 RPSで運用することをお勧めします。
同時実行 | P50 | P95 | P99 | RPS | 成功 | 容量 |
|---|---|---|---|---|---|---|
5 | 80 ms | 120 ms | 150 ms | 45 | 100% | 10% |
10 | 85 ms | 140ms | 180 ms | 88 | 100% | 20% |
20 | 95 ms | 180 ms | 250 ms | 165 | 99.8% | 35% |
50 | 150 ms | 350 ms | 500 ms | 310 | 99.2% | スイートスポット:65% |
100 | 250 ms | 800 ms | 1.2秒 | 420 | 97.5% | 90% ⚠️ 最大値に近づいています |
150 | 450 ms | 1.5秒 | 2.5s | 480 | 95.0% | 100% 最大 RPS 無効 |
最大 RPS で動作させると、次の問題が発生することがあります:
- レイテンシーの低下。例では、容量65%ではP95は350ミリ秒ですが、容量100%では1.5秒です。
- トラフィックの急増に対応する余裕がありません。容量が100%の状態では、わずかなスパイクでもタイムアウトが発生します。稼働率65%の場合、トラフィックが50%急増しても問題なく処理できます。
- エラー率が高くなっています。例では、成功率は65%の容量で99.2%ですが、100%の容量では、5%の失敗率で95.0%です。
- リソースが枯渇する恐れがあります。最大負荷時、キューが増加し、メモリ負荷が増大し、接続プールが飽和し始め、インシデント発生後の回復時間が増加します。
以下の表に、異なるユースケースの推奨動作点を示します。
ユースケース | ターゲット容量 | 根拠 |
|---|---|---|
レイテンシーに影響を受けやすい(検索、チャット) | 最大値の50~60% | 低P95/P99レイテンシを優先します。 |
バランス(推奨) | 最大値の60~70% | コストとレイテンシーの良好なバランス |
コスト最適化(バッチジョブ) | 最大値の70~80% | 許容される高いレイテンシー |
非推奨 | 最大値の85%超 | レイテンシーの急増、バースト容量なし |
動作点およびエンドポイントサイズの計算用ヘルパー関数
- Find the optimal point
- Size recommendation formula
次のコードはQPSとP95レイテンシーをプロットします。プロットでは、曲線が急に上向きに曲がり始める点を探してください。最適な動作点です。
import matplotlib.pyplot as plt
# Plot QPS vs. P95 latency
qps_values = [45, 88, 165, 310, 420, 480]
p95_latency = [120, 140, 180, 350, 800, 1500]
plt.plot(qps_values, p95_latency, marker='o')
plt.axvline(x=310, color='green', linestyle='--', label='Optimal (65% capacity)')
plt.axvline(x=480, color='red', linestyle='--', label='Maximum (100% capacity)')
plt.xlabel('Queries Per Second (QPS)')
plt.ylabel('P95 Latency (ms)')
plt.title('QPS vs. Latency: Finding the Sweet Spot')
plt.legend()
plt.grid(True)
plt.show()
def calculate_endpoint_size(target_qps, optimal_capacity_percent=0.65):
"""
Calculate required endpoint capacity
Args:
target_qps: Your expected peak production QPS
optimal_capacity_percent: Target utilization (default 65%)
Returns:
Required maximum endpoint QPS
"""
required_max_qps = target_qps / optimal_capacity_percent
# Add 20% safety margin for unexpected bursts
recommended_max_qps = required_max_qps * 1.2
return {
"target_production_qps": target_qps,
"operate_at_capacity": f"{optimal_capacity_percent*100:.0f}%",
"required_max_qps": required_max_qps,
"recommended_max_qps": recommended_max_qps,
"burst_capacity": f"{(1 - optimal_capacity_percent)*100:.0f}% headroom"
}
# Example
result = calculate_endpoint_size(target_qps=200)
print(f"Target production QPS: {result['target_production_qps']}")
print(f"Size endpoint for: {result['recommended_max_qps']:.0f} QPS")
print(f"Operate at: {result['operate_at_capacity']}")
print(f"Available burst capacity: {result['burst_capacity']}")
# Output:
# Target production QPS: 200
# Size endpoint for: 369 QPS
# Operate at: 65%
# Available burst capacity: 35% headroom
埋め込みモデルのボトルネックを特定する
インデックスがマネージド埋め込みを使用している場合、ロードテストノートブックは、各クエリでdebug_level=1パラメーターを介してコンポーネントごとのタイミングをキャプチャします。結果テーブルには、以下が含まれます。
ann_time近似最近傍検索に要した時間embedding_gen_time— モデルサービングエンドポイントでのクエリー埋め込み生成にかかった時間reranker_time再ランク付けの所要時間(有効な場合)response_time— 合計エンドツーエンド応答時間
ann_timeに比べてembedding_gen_timeが継続的に大きい場合、ボトルネックとなるのは埋め込みエンドポイントであり、AI Search エンドポイントではありません。よくある原因:
- 埋め込みモデルサービングエンドポイントは **Scale to zero** が有効になっています。本番運用負荷テスト向けに無効にしてください。「本番運用でのゼロにスケール回避」を参照してください。
- 埋め込みエンドポイントでは、テスト中のクエリレートに対し、プロビジョニング済み同時実行が不足しています。
- 埋め込みモデルのエンドポイントは他のワークロードと共有されています。ロード テストには専用のエンドポイントを使用してください。
AI検索のパフォーマンスを埋め込みモデルのパフォーマンスから分離するには、負荷テストのために セルフマネージド型埋め込み に切り替えてください。テキストクエリの代わりに、EMBEDDING_COLUMNパラメーターで事前計算済みベクトルを渡してください。これにより、埋め込みレイテンシーが測定から完全に排除されます。
ステップ 7: エンドポイントのサイズを決定する
ノートブックのおすすめを使用します
結果を分析した後、ノートブックは次の操作を求めます。
- 最適なレイテンシー要件を満たす行を選択してください。
- アプリケーションの希望するRPSを入力してください。
ノートブックはその後、推奨エンドポイントサイズを表示します。以下に基づいて必要な容量を計算します:
- 目標RPS
- 異なる同時実行レベルで観測されたレイテンシー
- 成功率しきい値
- 安全マージン (通常、想定されるピーク負荷の2倍)
スケーリングに関する考慮事項
標準エンドポイント:
- インデックスサイズに対応するよう、自動的にスケールアップします。
- スループットをサポートするために、手動でスケールアップしてください。
- インデックスが削除されると、自動的にスケールダウンされます。
- 容量を削減するには、手動でスケールダウンしてください。
ストレージ最適化エンドポイント:
- インデックスサイズに対応するよう、自動的にスケールアップします。
- インデックスが削除されると、自動的にスケールダウンされます。
ステップ 8: 最終構成を検証する
エンドポイント構成の更新後:
- エンドポイントが準備できるまでお待ちください。これには数分かかる場合があります。
- ノートブックで最終検証テストを実行します。
- 性能が要件を満たしていることを確認します:
- RPS が目標スループット以上です
- P95 レイテンシが SLA を満たします。
- 成功率 > 99.5%
- 継続的なエラーはありません。
検証に失敗した場合は、以下をお試しください。
- エンドポイントの容量を増やします。
- クエリー複雑性の最適化
- フィルターパフォーマンスの確認
- 埋め込みモデルのエンドポイントの設定を確認
再テストの時期
パフォーマンスの可視性を維持するため、四半期ごとにベースライン負荷テストを実行するのがよいでしょう。次の変更のいずれかを行った場合も:
- クエリパターンまたは複雑さの変更
- AI検索インデックスを更新する
- フィルター構成を変更する
- 大幅なトラフィックの増加が予想されます。
- 新しい機能または最適化をデプロイ
- 標準からストレージ最適化エンドポイントタイプへの変更
トラブルシューティング
すべてのリクエストが~10ミリ秒のレイテンシーと240バイトの応答で失敗します
これは、サービスプリンシパルが401/403応答を受信していることを示しています。認証:
- サービスプリンシパルは、AI検索エンドポイントに対し **クエリ実行可能** 権限を持っています(インデックスだけでなく)。
- シークレットスコープには、有効な
service_principal_client_idとservice_principal_client_secretキーが含まれています。 - OAuthシークレットは有効期限が切れていません。
ノートブックには、完全な負荷テストを実行する前にこれを捕捉する接続チェックが含まれています。
同じクラスターでの複数の負荷テストジョブの実行
同じクラスター上で 2 つのロード テスト ジョブを同時に実行すると、1 つのジョブが古いOAuthトークンを受け取るか、他のジョブの Locust ワーカーとの CPU 競合が発生する可能性があります。 信頼性の高い結果を得るには、負荷テストジョブを専用クラスター上で 一度に1つずつ 実行してください。
コンポーネントタイミンググラフが空です
コンポーネントのタイミンググラフ(ann_time、embedding_gen_time、reranker_time)は、クエリ応答でdebug_infoを返す必要があります。これらのグラフが空の場合:
fast_vs_load_test_async_load.pyスクリプト(応答からdebug_infoを解析する)をlocust_script_pathとして使用していることを確認してください。- 一部のエンドポイント構成では、
debug_infoが返されない場合があります。自己管理型のエンべディングインデックスは通常、ann_timeとresponse_timeを返しますが、embedding_gen_timeやreranker_timeは返しません。
結果テーブルはSQLウェアハウスからクエリできません
ノートブックは、クラスターのSparkセッションから結果を書き込みます。ノートブックではデータが入力されていると報告しているにもかかわらず、SQLウェアハウスがテーブルに対して0行を表示している場合、問題はUnity Catalogのメタデータ同期の遅延である可能性があります。数分待って再試行してください、または、同じクラスターにアタッチされたノートブックからテーブルを直接クエリしてください。
ベストプラクティスの概要
構成をテストする
-
ピーク負荷時に少なくとも5分間テストを実行してください。
-
認証にはOAuthサービスプリンシパルを使用してください。
-
予想される本番運用クエリに一致する、現実的なクエリペイロードを作成します。
-
本番運用に近いフィルターとパラメーターをテストします。
-
測定する前にウォームアップ期間を含めてください。
-
複数の同時実行レベルでテストしてください。
-
平均だけでなく、P95/P99レイテンシーも追跡してください。
-
キャッシュされたパフォーマンスとキャッシュされていないパフォーマンスの両方をテストします。
Python# Conservative approach: Size endpoint for UNCACHED performance
uncached_results = run_load_test(diverse_queries, duration=600)
endpoint_size = calculate_capacity(uncached_results, target_rps=500)
# Then verify cached performance is even better
cached_results = run_load_test(repetitive_queries, duration=300)
print(f"Cached P95: {cached_results['p95']}ms (bonus performance)")
クエリセットの設計
- テストクエリの多様性を実際のトラフィック分布(よく使用されるクエリとまれなクエリ)と一致させます。
- ログから実際のクエリを使用します(匿名化済み)。
- さまざまなクエリの複雑度を含めます。
- キャッシュされたシナリオとキャッシュされていないシナリオの両方をテストし、結果を個別に追跡してください。
- 期待されるフィルターの組み合わせでテストします。
- 本番運用で使用するのと同じパラメーターをご使用ください。たとえば、本番運用でハイブリッド検索を使用する場合、ハイブリッド検索クエリを含めます。本番運用と同様の
num_resultsパラメーターを使用します。 - 本番運用で発生しないクエリーは使用しないでください。
パフォーマンスの最適化
レイテンシーが高すぎる場合は、以下をお試しください。
- OAuth サービスプリンシパルを使用 (PAT ではなく) - 100 ms の改善
num_resultsを削減:100件の結果取得は10件よりも遅い- フィルターの最適化:複雑すぎたり、制限が厳しすぎるフィルターは、クエリの速度を低下させます。
- エンべディングエンドポイントの確認:ゼロにスケールダウンされていないか、十分な帯域幅があることを確認してください。
レート制限に達している場合は、以下をお試しください。
- エンドポイント容量を増やす:エンドポイントをスケールアップする
- クライアント側のレート制限を実装するか、クエリを時間的に分散させます。
- 接続プーリングによる接続の再利用
- 再試行ロジックを追加する - 指数関数的バックオフを使用する(Python SDK に既に含まれています)