2

私はクエリを持っています(これは非常に遅い〜2,5秒です):

db.markers.find({ latlng: { '$within': { '$box': [ [ -16, -140 ], [ 75, 140 ] ] } } }).sort({_id: -1}).limit(1000)

このクエリのexplainを実行すると、次のようになります。

{
   "cursor" : "GeoBrowse-box",
   "isMultiKey" : false,
   "n" : 1000,
   "nscannedObjects" : 242331,
   "nscanned" : 242331,
   "nscannedObjectsAllPlans" : 242331,
   "nscannedAllPlans" : 242331,
   "scanAndOrder" : true,
   "indexOnly" : false,
   "nYields" : 1383,
    "nChunkSkips" : 0,
    "millis" : 2351,
    "indexBounds" : {
        "latlng" : [ ]
    },
    "lookedAt" : NumberLong(262221),
    "matchesPerfd" : NumberLong(242331),
    "objectsLoaded" : NumberLong(242331),
    "pointsLoaded" : NumberLong(0),
    "pointsSavedForYield" : NumberLong(0),
    "pointsChangedOnYield" : NumberLong(0),
    "pointsRemovedOnYield" : NumberLong(0),
    "server" : "xx:27017"
}

sort({_ id:-1})を削除すると、explainが表示されます(高速クエリ5 milis):

{
    "cursor" : "GeoBrowse-box",
    "isMultiKey" : false,
    "n" : 1000,
    "nscannedObjects" : 1000,
    "nscanned" : 1000,
    "nscannedObjectsAllPlans" : 1000,
    "nscannedAllPlans" : 1000,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 5,
    "indexBounds" : {
        "latlng" : [ ]
    },
    "lookedAt" : NumberLong(1000),
    "matchesPerfd" : NumberLong(1000),
    "objectsLoaded" : NumberLong(1000),
    "pointsLoaded" : NumberLong(0),
    "pointsSavedForYield" : NumberLong(0),
    "pointsChangedOnYield" : NumberLong(0),
    "pointsRemovedOnYield" : NumberLong(0),
        "server" : "xx:27017"
}

私はlatlngに2dインデックス、_idにdescインデックス、複合インデックスを持っています。

db.markers.ensureIndex({latlng: '2d', _id:-1})
db.markers.ensureIndex({ latlng: '2d' })
db.markers.ensureIndex({ _id: -1 })

私が達成したいのは、特定の領域から最新のものからソートされたマーカーを取得することです。

2.5秒未満の方法でアイデアや提案はありますか?

誰かが自分のテストをしたい場合

var i = 0,
  lat = 0,
  lng = 0;

for (i; i < 260000; i++) {
  lat = parseFloat(Math.min(-90 + (Math.random() * 180), 90).toFixed(6));
  lng = parseFloat(Math.min(-180 + (Math.random() * 360), 180).toFixed(6));
  collection.insert({latlng: [lat, lng]}, function () {});
}

collection.find({ latlng: { '$within': { '$box': [ [ -90, -180 ], [ 90, 180 ] ] } } }, {latlng: 1, _id: 1 }).sort({_id: -1}).limit(1000).explain()

私のローカルマシンで受け取る(〜2,6s):

{
    "cursor" : "GeoBrowse-box",
    "isMultiKey" : false,
    "n" : 1000,
    "nscannedObjects" : 260000,
    "nscanned" : 260000,
    "nscannedObjectsAllPlans" : 260000,
    "nscannedAllPlans" : 260000,
    "scanAndOrder" : true,
    "indexOnly" : false,
    "nYields" : 1612,
    "nChunkSkips" : 0,
    "millis" : 2613,
    "indexBounds" : {
            "latlng" : [ ]
    },
    "lookedAt" : NumberLong(260000),
    "matchesPerfd" : NumberLong(260000),
    "objectsLoaded" : NumberLong(260000),
    "pointsLoaded" : NumberLong(0),
    "pointsSavedForYield" : NumberLong(0),
    "pointsChangedOnYield" : NumberLong(0),
    "pointsRemovedOnYield" : NumberLong(0),
    "server" : "xx:27017"
}

どうも

4

2 に答える 2

7

コレクションに次の3つのインデックスが実際に定義されていますか?

db.markers.ensureIndex({ latlng: '2d', _id:-1 })
db.markers.ensureIndex({ latlng: '2d' })
db.markers.ensureIndex({ _id: -1 })

地理空間インデックスのドキュメントでは、同じコレクションに複数の地理インデックスを作成しないようにアドバイスしています。MongoDBはそれを許可しますが、動作は望ましくない場合があります。{latlng: '2d'}あなたの場合の私の推測では、複合インデックスの代わりに非複合が選択された可能性があります。出力は、インデックス名の代わりに単にexplain()レポートするため、ここでは実際には役に立ちません。GeoBrowse-boxただし、カーソルが複合インデックスを使用することを手動で示唆し、結果が改善されるかどうかを確認することをお勧めします。または、単純に非複合インデックスを削除{latlng: '2d', _id:-1}します。これは、クエリオプティマイザの明白で唯一の選択肢だからです。

最後に、{_id: -1}インデックスは冗長であり、削除できます。複合インデックスのドキュメントによると、方向は、複数のフィールドで構成されるインデックスを処理する場合にのみ関係します。シングルキーインデックスの場合、インデックスを簡単に前後に移動できます。MongoDBは{_id: 1}デフォルトですでにインデックスを作成しているので、単にそれに依存する方が効率的です。

ここで、インデックス作成が邪魔にならないようにします。クエリの1つの注意点は、非地理基準(_idこの場合)で並べ替える前に、地理空間クエリコンポーネントに制限が適用されることです。これは、結果が実際にで並べ替えられる一方_idで、その並べ替えでは、一致する範囲内のすべてのドキュメントが考慮されない可能性があることを意味すると思います。これは、保留中のソリューションとしてSERVER-4247を参照しているドキュメントの複合インデックスビットに記載されています。


編集:ベンチマークのフォローアップ

サンプルデータを入力しました。これは、±90〜±180の260kのランダムポイントです。次に、クエリを実行しました。

db.markers.find(
  { latlng: { $within: { $box: [[-90, -180], [90, 180]] }}},
  { latlng: 1, _id: 1 }
).sort({_id: -1}).limit(1000).explain()

これには1713ミリ秒かかりました(2351ミリ秒の時間ではなく、比較のベースラインとして使用します)。また、クエリがすべての260kドキュメントに一致し、同じ数のインデックスエントリをスキャンしたことにも注意してください。ソートまで制限が考慮されていなかったようですが、これはここ_idのメモに基づいて予想したものではありません。次に、クエリを少し調整して、他のいくつかのケースを調べました。

  • _id並べ替えと制限のない元のクエリ:nscannedは260k、時間は1470msです。
  • _idソートなしの元のクエリ:nscannedは1000、時間は9msです。
  • 制限のない元のクエリ:nscannedは260k、時間は2567msです。

_idまた、インデックス付けされていないフィールドのみで並べ替えをテストして、地理的一致の後に並べ替えで何が起こるかをシミュレートしたいと思いました。ただし、_idデフォルトのインデックスが常に存在するため、使用できませんでした。latlngこれを行うには、複合地理インデックスを削除してから、オブジェクトで並べ替えました。これにより、nscanned260kと1039msの時間が得られました。1000の制限を追加すると、時間は461msでした。

これを上記の1470ms(並べ替えと制限のないgeoクエリ)に追加すると、制限のない元のクエリである2567msに非常に近くなります。同様に、461ms(限定ソート)を1470msに追加すると、元のベンチマーク結果である1713msに近くなります。その相関関係に基づいて_id、ベンチマークのソートが複合インデックスをまったく利用していないことを賭けます。

いずれにせよ、ベンチマークが遅いもう1つの理由は、地理的な一致が非常に広いためです。境界が厳しくなると、ソートがインデックスに登録されていない場合でも、ソートするデータが確実に少なくなります。とはいえ、サーバー-4247は、地理一致を実行する前に非地理ソートを最初に処理する可能性が高いため、役立つと思います。

于 2012-10-19T18:08:55.300 に答える
0

インデックスは複合キーを使用していますか?

db.markers.ensureIndex({latlng: '2d', _id:-1})
于 2012-10-16T07:56:34.993 に答える