Query Explain を使用してクエリのパフォーマンスを把握する

Query Explain を使用すると、Firestore クエリをバックエンドに送信し、返信でバックエンド クエリの実行に関する詳細なパフォーマンス統計情報を受け取ることができます。これは、多くのリレーショナル データベース システムの EXPLAIN [ANALYZE] オペレーションのように機能します。

Query Explain リクエストは、Firestore サーバー クライアント ライブラリを使用して送信できます。

Query Explain の結果は、非効率性やサーバー側でボトルネックになっている可能性のある箇所を示しており、クエリがどのように実行されるかを理解するうえで役立ちます。

Query Explain:

  • クエリ インデックスを調整して効率性を向上できるように、クエリ計画フェーズに関する分析情報を提供します。
  • 分析オプションを使用すると、クエリごとに費用とパフォーマンスを把握でき、さまざまなクエリパターンをすばやく反復して使用を最適化できます。

Query Explain のオプションについて: デフォルトと分析

Query Explain のオペレーションは、「デフォルト」オプションまたは「分析」オプションを使用して実行できます。

デフォルトのオプションでは、Query Explain はクエリを計画するものの実行ステージはスキップします。これによりプランナー ステージの情報が返されます。この情報を使用すると、クエリに必要なインデックスがあることを確認して、どのインデックスが使用されているかを把握できます。これは、たとえば、特定のクエリがさまざまなインデックスを交差させることなく、複合インデックスを使用していることを確認するのに役立ちます。

分析オプションを使用すると、Query Explain はクエリの計画と実行の両方を行います。これにより、クエリ実行ランタイムからの統計情報とともに前述のすべてのプランナー情報が返されます。これには、クエリ実行に関するシステムレベルの分析情報とともにクエリのお支払い情報が含まれます。このツールを使用すると、さまざまなクエリとインデックスの構成をテストしてコストとレイテンシを最適化できます。

Query Explain のコストは?

デフォルト オプションで Query Explain を使用すると、インデックスと読み取りのオペレーションは実行されません。クエリの複雑さに関係なく、1 回の読み取りオペレーションが課金されます。

分析オプションで Query Explain を使用すると、インデックスと読み取りのオペレーションが実行されるため、通常どおりクエリに対して課金されます。分析アクティビティに追加料金はかからず、実行されるクエリに対する通常の料金のみにかかります。

デフォルトのオプションで Query Explain を使用する

クライアント ライブラリを使用して、デフォルトのオプション リクエストを送信できます。

リクエストは、通常のクエリ オペレーションと同じ権限を使用して、IAM で認証されることに注意してください。Firebase Authentication などの他の認証手法は無視されます。詳細については、サーバー クライアント ライブラリの IAM に関するガイドをご覧ください。

Java(管理)

Query q = db.collection("col").whereGreaterThan("a", 1);
ExplainOptions options = ExplainOptions.builder().build();

ExplainResults<QuerySnapshot> explainResults = q.explain(options).get();
ExplainMetrics metrics = explainResults.getMetrics();
PlanSummary planSummary = metrics.getPlanSummary();

    
Node(管理者)

const q = db.collection('col').where('country', '=', 'USA');
const options = { analyze : 'false' };

const explainResults = await q.explain(options);

const metrics = explainResults.metrics;
const plan = metrics.planSummary;

    

レスポンスの正確な形式は、実行環境によって異なります。 返された結果は JSON に変換できます。次に例を示します。

{
    "indexes_used": [
        {"query_scope": "Collection", "properties": "(category ASC, __name__ ASC)"},
        {"query_scope": "Collection", "properties": "(country ASC, __name__ ASC)"},
    ]
}

詳細については、Query Explain のレポートのリファレンスをご覧ください。

分析オプションで Query Explain を使用する

クライアント ライブラリを使用して、分析オプション リクエストを送信できます。

リクエストは、通常のクエリ オペレーションと同じ権限を使用して、IAM で認証されることに注意してください。Firebase Authentication などの他の認証手法は無視されます。詳細については、サーバー クライアント ライブラリの IAM に関するガイドをご覧ください。

Java(管理)

Query q = db.collection("col").whereGreaterThan("a", 1);

ExplainOptions options = ExplainOptions.builder().setAnalyze(true).build();

ExplainResults<QuerySnapshot> explainResults = q.explain(options).get();

ExplainMetrics metrics = explainResults.getMetrics();
PlanSummary planSummary = metrics.getPlanSummary();
List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
ExecutionStats stats = metrics.getExecutionStats();

    
Node(管理者)

const q = db.collection('col').where('country', '=', 'USA');

const options = { analyze : 'true' };

const explainResults = await q.explain(options);

const metrics = explainResults.metrics;
const plan = metrics.planSummary;
const indexesUsed = plan.indexesUsed;
const stats = metrics.executionStats;

    

次の例は、planInfo に加えて返される stats オブジェクトを示しています。レスポンスの正確な形式は、実行環境によって異なります。レスポンスの例は JSON 形式です。

{
    "resultsReturned": "5",
    "executionDuration": "0.100718s",
    "readOperations": "5",
    "debugStats": {
               "index_entries_scanned": "95000",
               "documents_scanned": "5"
               "billing_details": {
                     "documents_billable": "5",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }

}

詳細については、Query Explain のレポートのリファレンスをご覧ください。

結果を解釈して調整を行う

プロダクションのジャンルや国でムービーをクエリするサンプル シナリオを見てみましょう。

たとえば、以下の SQL クエリと同等のものを想定します。

SELECT *
FROM /movies
WHERE category = 'Romantic' AND country = 'USA';

分析オプションを使用すると、返された指標では、2 つの単一フィールド インデックス((category ASC, __name__ ASC)(country ASC, __name__ ASC))でクエリが実行されることが示されます。16,500 件のインデックス エントリをスキャンしますが、ドキュメントは 1,200 件しか返されません。

// Output query planning info
{
    "indexes_used": [
        {"query_scope": "Collection", "properties": "(category ASC, __name__ ASC)"},
        {"query_scope": "Collection", "properties": "(country ASC, __name__ ASC)"},
    ]
}

// Output query status
{
    "resultsReturned": "1200",
    "executionDuration": "0.118882s",
    "readOperations": "1200",
    "debugStats": {
               "index_entries_scanned": "16500",
               "documents_scanned": "1200"
               "billing_details": {
                     "documents_billable": "1200",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }
}

クエリの実行パフォーマンスを最適化するには、完全に対象となる複合インデックス (category ASC, country ASC, __name__ ASC) を作成します。

分析オプションを指定してクエリを再度実行すると、このクエリに対して新しく作成されたインデックスが選択され、クエリが高速かつ効率的に実行されていることを確認できます。

// Output query planning info
{
    "indexes_used": [
        {"query_scope": "Collection", "properties": "(category ASC, country ASC,  __name__ ASC)"}
    ]
}

// Output query stats
{
    "resultsReturned": "1200",
    "executionDuration": "0.026139s",
    "readOperations": "1200",
    "debugStats": {
               "index_entries_scanned": "1200",
               "documents_scanned": "1200"
               "billing_details": {
                     "documents_billable": "1200",
                     "index_entries_billable": "0",
                     "small_ops": "0",
                     "min_query_cost": "0",
               }
    }
}