8

コレクション イベントには、userId とイベントの配列があります。配列内の各要素は埋め込みドキュメントです。例:

{
    "_id" : ObjectId("4f8f48cf5f0d23945a4068ca"),
    "events" : [
            {
                    "eventType" : "profile-updated",
                    "eventId" : "247266",
                    "eventDate" : ISODate("1938-04-27T23:05:51.451Z"),
            },
           {
                   "eventType" : "login",
                   "eventId" : "64531",
                   "eventDate" : ISODate("1948-05-15T23:11:37.413Z"),
           }
    ],
    "userId" : "junit-19568842",

}

次のようなクエリを使用して、過去 30 日間に生成されたイベントを検索します。

db.events.find( { events : { $elemMatch: { "eventId" : 201, 
"eventDate" : {$gt : new Date(1231657163876) } } } }  ).explain()

クエリ プランは、テスト データに含まれるイベントが少ない (約 20) 場合に、"events.eventDate" のインデックスが使用されることを示しています。

{
    "cursor" : "BtreeCursor events.eventDate_1",
    "nscanned" : 0,
    "nscannedObjects" : 0,
    "n" : 0,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : true,
    "indexOnly" : false,
    "indexBounds" : {
            "events.eventDate" : [
                    [
                            ISODate("2009-01-11T06:59:23.876Z"),
                            ISODate("292278995-01--2147483647T07:12:56.808Z")
                    ]
            ]
    }

}

ただし、多数のイベント (約 500) がある場合、インデックスは使用されません。

{
    "cursor" : "BasicCursor",
    "nscanned" : 4,
    "nscannedObjects" : 4,
    "n" : 0,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {

    }

}

イベントが多いのに、インデックスが使用されないのはなぜですか? 多数のイベントがある場合、MongoDB はインデックスを使用するよりもすべてのアイテムをスキャンする方が効率的であると判断するのでしょうか?

4

2 に答える 2

13

MongoDBのクエリオプティマイザーは特別な方法で機能します。特定のクエリプランのコストを計算するのではなく、利用可能なすべてのプランを起動するだけです。最初に戻った方が最適と見なされ、将来使用されます。

アプリケーションが大きくなり、データが大きくなり、変化するため、ある時点で最適な計画が最適でなくなる可能性があります。そのため、mongoはそのクエリ選択プロセスを時々繰り返します。

この具体的なケースでは、基本的なスキャンが最も効率的だったようです。

リンク:http ://www.mongodb.org/display/DOCS/Query+Optimizer

于 2012-04-18T23:50:22.570 に答える
2

$hint を使用して、インデックス "events.eventDate" を強制的に使用すると、nscannedObjects はインデックスなし以上のものになります。

インデックスを使用する場合の擬似コード:

for(all entries in index matching the criteria) {
  get user object and scan to see if the eventId criteria is met
}

基準に一致するインデックス内のすべてのエントリ --> 各イベントはインデックス内のエントリです。したがって、インデックスのエントリ数はユーザー数よりも多くなります。4 つのユーザー オブジェクトがあり、条件に一致する合計 7 つのイベントがあるとします。ユーザー オブジェクトは 7 回スキャンされます (for ループは 7 回実行されます)。インデックスがスキャンされない場合、4 つのユーザー オブジェクトすべてが 1 回だけ検査されます。したがって、インデックスを使用すると、ユーザー オブジェクトがスキャンされる回数は、インデックスを使用しない場合よりも多くなります。この理解は正しいでしょうか?

db.events.find( { events : { $elemMatch: { "eventId" : 201, 
"eventDate" : {$gt : new Date(1231657163876) } } } }  )
._addSpecial("$hint",{"events.eventDate":1}).explain()

{
    "cursor" : "BasicCursor",
    "nscanned" : 7,
    "nscannedObjects" : 7,
    "n" : 0,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {

}
于 2012-04-19T20:29:13.607 に答える