このトピックでは、クエリプランの概要、クエリの再計画が発生する原因、およびその対処法について説明します。
クエリプランナー
MongoDB のクエリプランナーは、利用可能なインデックスに基づいて、各クエリに対して最も効率的なクエリプランを選択し、キャッシュします。次の図は、クエリプランナーのワークフローを示しています。
クエリプランナーは、クエリの実行に必要な作業単位 (works) の数に基づいて候補プランを評価し、最も効率的なクエリプランを決定します。キャッシュされたプランのエントリは、同じクエリシェイプを持つ後続のクエリで再利用できます。
クエリプランキャッシュ内のエントリは、次の 3 つの状態のいずれかになります:
Missing:プランがクエリプランキャッシュに存在しない状態。
Inactive:プランがクエリプランキャッシュに存在し、評価済みの
works値を持つ状態。Active な状態に昇格する可能性があります。Active:採用されたプランとしてクエリプランキャッシュに存在する状態。Inactive な状態に降格する可能性があります。
クエリプランキャッシュは完全にメモリに保存され、ディスクには永続化されません。キャッシュは、MongoDB インスタンスが再起動するたびにクリアされます。コレクションまたはインデックスを削除した場合も、クエリプランキャッシュはクリアされます。キャッシュにはサイズ制限があり、Least Recently Used (LRU) の置き換えポリシーが使用されます。つまり、使用頻度の低いエントリは定期的に削除されます。
特定のケースでは、次のコマンドを実行してクエリプランを管理できます:
特定のコレクションのクエリプランキャッシュをクリアします。
db.<collection>.getPlanCache().clear()特定のコレクションのすべてのクエリシェイプをリスト表示します。
db.<collection>.getPlanCache().listQueryShapes()特定のクエリシェイプのキャッシュされたプランを表示します。
db.<collection>.getPlanCache().getPlansByQuery({"query": {"name": "testname"}, "sort": { "name": 1 })
QueryHash と planCacheKey
MongoDB 4.2 以降、MongoDB はクエリシェイプを識別するために queryHash を導入しました。MongoDB は、異なるクエリシェイプごとに一意の queryHash を生成します。queryHash とは異なり、planCacheKey はクエリシェイプとそのシェイプで現在利用可能なインデックスの両方の関数です。クエリシェイプをサポートするインデックスを追加または削除すると、planCacheKey は変更される可能性がありますが、queryHash は変更されません。
例えば、次のインデックスとクエリシェイプを持つコレクションを考えます:
インデックス
db.foo.createIndex( { x: 1 } ) db.foo.createIndex( { x: 1, y: 1 } ) db.foo.createIndex( { x: 1, z: 1 }, { partialFilterExpression: { x: { $gt: 10 } } } )クエリシェイプ
db.foo.explain().find( { x: { $gt: 5 } } ) // クエリ操作 1 db.foo.explain().find( { x: { $gt: 20 } } ) // クエリ操作 2
3 番目のインデックスはクエリ 2 をサポートしますが、クエリ 1 はサポートしません。したがって、2 つのクエリは完全に異なるクエリプランキャッシュキーを持ちます。新しいインデックス {x:1, a:1} が追加されると、これらの 2 つのクエリプランキャッシュキーも更新されます。
クエリの再計画
コレクション内のデータが変更されると、キャッシュされたクエリプランが最適でなくなることがあります。データは動的であるため、クエリプランもそれに応じて適応する必要があります。
キャッシュされたプランと同じクエリシェイプを持つクエリを実行すると、クエリプランナーは候補プランを再評価しません。代わりに、キャッシュされたプランを直接使用してクエリを実行します。同時に、クエリプランナーはプランのパフォーマンスを継続的に評価します。プランナーが、キャッシュされたプランが代替プランよりも著しく非効率である (例えば、10 倍以上遅い) と判断した場合、実行を中止し、非効率なプランをキャッシュから削除し、すべての候補プランを再評価します。このプロセスをクエリの再計画と呼びます。
影響とソリューション
スロークエリログに "replanned":true というキーワードが表示されることがあります。これは、データベースが特定のクエリシェイプのクエリ条件に対して一貫して効果的なプランを提供できないことを示します。
以下はスロークエリログの例です:
"replanned":true,"replanReason":"cached plan was less efficient than expected: expected trial execution to take X works but it took at least 10X works"影響
頻繁なクエリの再計画は、クエリのパフォーマンスを低下させる可能性があります。
大量のクエリの再計画は、mutex ロックの競合を引き起こし、高い CPU 使用率の原因となる可能性があります。
ソリューション
一時的にインスタンスの仕様をアップグレードして、データベースの負荷を軽減します。詳細については、「インスタンス構成の変更」をご参照ください。
クエリプランキャッシュをクリアして、クエリプランナーがより良いプランを選択できるかを確認してみてください。
アプリケーションで
hint()を使用して、再計画を引き起こすクエリ条件にインデックスを指定します。以下に例を示します。db.<collection>.find({a:"ABC"},{b:1,_id:0}).sort({c:1}).hint({ a: 1, c: 1, b: 1} )説明ヒントを使用すると、クエリプランナーのデフォルトの動作がオーバーライドされます。
サーバー側でインデックスフィルターを使用して、再計画をトリガーするクエリで利用可能なインデックスを制限します。例:
// インデックスが存在するかどうかを確認 db.<collection>.getIndexes() // インデックスフィルターを設定 db.runCommand( { planCacheSetFilter: "<collection>", query: { a: "ABC" }, projection: { b: 1, _id: 0 }, sort: { c: 1 }, indexes: [ { a: 1, c: 1 , b: 1 } ] } ) // 以前のフィルターを削除 db.runCommand( { planCacheClearFilters: "<collection>" } )説明インデックスフィルターは、クエリプランナーのデフォルトの動作をオーバーライドします。
クエリにヒントとインデックスフィルターの両方がある場合、インデックスフィルターが優先されます。したがって、インデックスフィルターは慎重に使用してください。詳細については、「Index Filters」をご参照ください。
クエリオプティマイザーは、コレクションスキャン (COLLSCAN) または
planCacheSetFilterで指定されたインデックスのいずれかを使用してクエリプランを生成します。指定されたインデックスが存在しないか非表示の場合、オプティマイザーはコレクションスキャンを使用します。これにより、CPU 使用率の高騰や I/O スパイクなど、インスタンスに深刻なパフォーマンスの問題が発生する可能性があります。したがって、planCacheSetFilter操作を実行する前に、db.<collection>.getIndexes()を使用してインデックスが存在するかどうかを確認する必要があります。詳細については、「planCacheSetFilter」をご参照ください。
(推奨) クエリを最適化し、効率的なインデックスを作成して、クエリの再計画を回避します。
説明ヒントやインデックスフィルターの使用は最善のソリューションではありません。ほとんどの場合、クエリ文、利用可能なインデックス、およびドキュメントスキーマを見直して修正する方が、より良い結果が得られます。
(推奨) ご利用の MongoDB インスタンスがメジャーバージョン 4.2 または 4.4 を実行している場合、インスタンスを最新のカーネルマイナーバージョンに更新して、mutex ロックの競合を減らすことを推奨します。または、インスタンスをメジャーバージョン 5.0 または 6.0 にアップグレードして、これらの問題を解決することもできます。関連するカーネルの JIRA チケットの詳細については、「SERVER-40805」をご参照ください。マイナーバージョンの更新方法とメジャーバージョンのアップグレード方法については、「データベースのマイナーバージョンをアップグレードする」および「データベースのメジャーバージョンをアップグレードする」をご参照ください。
これらのソリューションで問題が解決しない場合は、してテクニカルサポートを依頼できます。