このトピックでは、クエリプランの定義について説明します。 このトピックでは、クエリの再計画の考えられる原因についても説明し、ソリューションを提供します。
クエリプランナー
クエリの場合、MongoDBクエリプランナーは、使用可能なインデックスに基づいて最も効率的なクエリプランを選択してキャッシュします。 次の図は、クエリプランナーの動作を示しています。
最も効率的なクエリプランの評価は、クエリプランナが候補プランを評価するときにクエリ実行プランによって実行されるワークユニット (ワーク) の数に基づきます。 クエリプランナにキャッシュされたクエリプランエントリは、同じクエリ形状を有するクエリに使用することができます。
クエリプランナにキャッシュされたクエリプランエントリは、次のいずれかの状態になります。
欠落: クエリプランキャッシュにエントリが存在しません。
Inactive: エントリはクエリプランキャッシュに存在し、評価後にワーク値が生成されます。 エントリはアクティブ状態に変換できます。
アクティブ: エントリはクエリプランキャッシュに存在します。 受賞したクエリプランは、非アクティブ状態に変換できます。
クエリプランキャッシュは、完全にメモリに格納され、持続しない。 キャッシュは、MongoDBデータベースが再起動されるたびにクリアされます。 コレクションまたはインデックスが削除されると、キャッシュもクリアされます。 クエリプランキャッシュは、サイズ制限を有し、LRU (least recently used) キャッシュ置換メカニズムに従います。 したがって、アクセス頻度が低いエントリは、時々キャッシュから追い出されます。
特定のケースでは、次のコマンドを実行してクエリプランを管理できます。
指定したコレクションからクエリプランキャッシュを消去します。
db.<collection>.getPlanCache().clear()
指定したコレクション内のすべてのクエリ図形を照会します。
db.<collection>.getPlanCache().listQueryShapes()
指定したクエリのクエリプランを照会します。
db.<collection>.getPlanCache().getPlansByQuery({"query": {"name": "testname"}, "sort": { "name": 1 })
queryHashとplanCacheKey
MongoDB 4.2以降では、クエリ形状を定義するためにqueryHashが導入されます。 各クエリシェイプは、queryHash値に関連付けられています。 MongoDB 4.2以降では、planCacheKeyも導入されます。 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.createIndex( { x: 1 } ) db.foo.createIndex( { x: 1, y: 1 } ) db.foo.createIndex( { x: 1, z: 1 }, { partialFilterExpression: { x: { $gt: 10 } } } )
3番目のインデックスはクエリ操作2のみをサポートできますが、クエリ操作1はサポートできません。 このように、2つのクエリは異なるplanCacheKey値を持ちます。 {x:1, a:1}
インデックスが作成された場合、両方のクエリ操作のplanCacheKey値が変更されます。
クエリの再計画
コレクションでデータの変更が発生した場合、キャッシュされたクエリプランはコレクションに適していません。 クエリプランがデータの変更と同期されるように、クエリプランを置き換える必要があります。
キャッシュされたクエリプランと同じクエリ形状を持つクエリプランを実行する場合、クエリプランナーはプランを計算せず、キャッシュされたクエリプランを直接使用します。 クエリプランナは、キャッシュされたクエリプランの実行効率を継続的に評価します。 クエリプランナが、キャッシュされたクエリプランの効率が別のクエリプランの10分の1を超えると判断した場合、クエリプランナは、キャッシュされたクエリプランを停止して追い出し、クエリプランを再評価します。 このプロセスは、クエリの再計画と呼ばれます。
影響と解決策
スロークエリログに "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"
影響
頻繁なクエリ再計画操作は、クエリのパフォーマンスを低下させます。
過度のクエリ再計画は、ミューテックスロックの競合につながる可能性があり、CPU使用率が高くなる可能性があります。
ソリューション
インスタンス仕様を一時的にアップグレードして、データベースの負荷を軽減します。 詳細については、「概要」をご参照ください。
クエリプランをクリアし、クエリアナライザーがより適切なクエリプランを選択できるかどうかを確認することを推奨します。
hint()
関数を使用して、ビジネスコードで再計画されるクエリ条件のインデックスを指定します。 例:db.<collection>.find({a:"ABC"},{b:1,_id:0}).sort({c:1}).hint({ a:1, c:1, b:1} )
説明クエリにヒントが使用されている場合、クエリプランナーによって選択されたクエリプランは有効になりません。
インデックスフィルターを使用して、ビジネスコードで再計画されるクエリ条件に使用するインデックスを制限します。 例:
// Configure index filtering. db.runCommand( { planCacheSetFilter: "<collection>", query: { a: "ABC" }, projection: { b: 1, _id: 0 }, sort: { c: 1 }, indexes: [ { a: 1, c: 1 , b: 1 } ] } ) // Delete existing configurations. db.runCommand( { planCacheClearFilters: "<collection>" } )
説明インデックスフィルターは、クエリプランを選択する際のクエリプランナーの予想される動作を上書きします。
クエリにヒントとインデックスフィルターを指定した場合、インデックスフィルターは指定されたヒントを上書きします。 したがって、インデックスフィルターは慎重に使用してください。 詳細については、「インデックスフィルター」をご参照ください。
(推奨) クエリを最適化し、クエリの再計画を防ぐためにクエリの効果的なインデックスを作成します。
説明ヒントやインデックスフィルターを使用する代わりに、クエリステートメント、使用可能なインデックス、およびドキュメントパターンを確認および変更することをお勧めします。
(推奨) インスタンスのメジャーバージョンがMongoDB 4.2またはMongoDB 4.4の場合、インスタンスのマイナーバージョンを最新のマイナーバージョンに更新することを推奨します。 これにより、mutexロックの使用を大幅に減らすことができます。 インスタンスのメジャーバージョンを5.0または6.0にアップグレードして、上記の問題を解決することもできます。 関連するカーネルJIRAチケットの詳細については、「SERVER-40805」をご参照ください。 インスタンスのマイナーバージョンを更新し、インスタンスのメジャーバージョンをアップグレードする方法の詳細については、「ApsaraDB For MongoDBインスタンスのマイナーバージョンの更新」および「ApsaraDB for MongoDBインスタンスのメジャーバージョンのアップグレード」をご参照ください。
上記の方法を使用した後に問題が持続する場合は、
チケットを起票し、テクニカルサポートにお問い合わせください。