web-dev-qa-db-ja.com

MongoDBでは、インデックスを使用した並べ替えはどのように機能しますか?

MongoDBでインデックスを使用した並べ替えが実際にどのように機能するのか疑問に思っています。 MongoDBのドキュメントには couplearticles がありますが、実際にはソートの進行方法や時間の複雑さについては説明されていません。 SOの検索と、これまでの一般的なインターウェブでは、関連するものは何も見つかりませんでした。

コレクションにaドキュメントがあり、find()句がbドキュメントに一致すると仮定します。cドキュメントが返されました、a>>b>> c、およびcは、返されたセットがメモリに収まらないほど適切に大きい数です。たとえば、1Mのドキュメントがあるとします。

操作の開始時に、ソートする必要があるbドキュメントと、サイズaのソート済みツリーインデックスが存在します。ドキュメントを並べ替える機能。

想像できます:

A)インデックスを順番にトラバースし、各ObjectIDについてbドキュメントのリストをトラバースします。 cに達するまで一致を返します。これはO(ab)になります。

B)はA)と同じですが、最初にbドキュメントでObjectIDのハッシュセットを作成します。これはO(a)ですが、O(b)メモリが必要です。

bドキュメントのセットのトラバースに基づいてソートを検討しようとしましたが、O(bよりも高速なものを思い付くことができません。 ログb)。これは、インデックスなしでソートするのと同じです。

私は(多分私は間違っていると思いますが)すべての並べ替えはインデックススキャンを必要としないと思います。

更新:

ケビンの答えと提供されたリンクは質問をかなり絞り込みますが、いくつかの点を確認/明確化したいと思います。

  1. 私が理解しているように、メモリ内の並べ替えを避けたい場合は、クエリと並べ替えに異なるインデックスを使用することはできません。 このページ を読んだとき、あなたはそれができるように見えました(または、少なくともどちらか一方を指定していませんでした)が、それは正しくないようです。基本的に、ドキュメントはクエリ中にインデックス順に検索され、したがってインデックス順に返されるため、ドキュメントは並べ替えられます。正しい?
  2. 複合インデックスでクエリを実行する場合、並べ替えインデックスは、クエリが等価であるインデックスを除いて、複合インデックスの最初のインデックスである必要があります。そうでない場合、ソートはメモリ内で実行されます。正しい?
  3. 並べ替えはどのように機能しますか$inまたは$orクエリ?たとえば、クエリが

    {a: {$in: [4, 6, 2, 1, 3, 10]}, b: {$gt: 1, $lt: 6}}

... abには、この順序で複合インデックスがあります。並べ替えがaまたはbの場合、並べ替えはどのように機能しますか? $orはさらに複雑です。私が理解しているように、$orクエリは基本的に複数の個別のクエリに分割されます。 $orクエリは、少なくとも個別のクエリの結果をマージするために、常にインメモリソートですか?

17
elhefe

MongoDBのインデックスはBツリー構造に格納され、各インデックスエントリはディスク上の特定の場所を指します。 Bツリー構造を使用することは、MongoDBインデックスがソートされた順序で格納され、常に順序どおりにトラバースされることを意味し、MongoDBがインデックスを介して一連のドキュメントをソートされた順序でフェッチするのが簡単です。

Update:Bツリー構造はMMAPv1ストレージエンジンに当てはまりますが、WiredTigerストレージエンジンによってわずかに異なる方法で実装されています(MongoDB 3.2以降のデフォルト)。基本的な考え方は変わりませんが、ソートされた順序でインデックスをトラバースする方が簡単です。

クエリのSORTステージ(つまり、メモリ内の並べ替え)は、32 MBのメモリ使用に制限されています。 SORTステージがこの制限を超えると、クエリは失敗します。この制限は、インデックスのソートされた性質を利用することで回避できます。これにより、MongoDBは、メモリ内のソートを実行せずにsort()パラメーターを使用してクエリを返すことができます。

クエリが次のような形であると仮定します。

_    db.a.find({b:{$gt:100}, c:{$gt:200}}).sort(...)
_

コレクションaには次のインデックスがあります:

_    db.a.createIndex({b:1,c:1})
_

クエリでsort()ステージが指定されている場合、2つのシナリオが考えられます。

1。 MongoDBはインデックスのソートされた性質を使用できず、メモリ内のSORTステージを実行する必要があります。

これは、クエリが「インデックスプレフィックス」を使用できない場合の結果です。例えば:

_    db.a.find({b:{$gt:100}, c:{$gt:200}}).sort({c:1})
_

上記のクエリでは、インデックス_{b:1,c:1}_を使用して次のことができます。

  • クエリの_{b:{$gt:100}}_部分について、100より大きいbを持つドキュメントに一致します。
  • ただし、返されるドキュメントがcでソートされている保証はありません。

したがって、MongoDBはメモリ内の並べ替えを実行する以外に選択肢はありません。このクエリのexplain()出力には、SORTステージがあります。このSORTステージは、32MBのメモリ使用に制限されます。

2。 MongoDBは、インデックスのソートされた性質を使用できます。

これは、クエリが以下を使用する場合の結果です。

  • インデックスの順序に一致するキーを並べ替えます。
  • インデックスと同じ順序を指定します(つまり、インデックス_{b:1,c:1}_はsort({b:1,c:1})またはsort({b:-1,c:-1})に使用できますが、sort({b:1,c:-1})には使用できません)

例えば:

_    db.a.find({b:{$gt:100}, c:{$gt:200}}).sort({b:1})
_

上記のクエリでは、インデックス_{b:1,c:1}_を使用して次のことができます。

  • クエリの_{b:{$gt:100}}_部分について、100より大きいbを持つドキュメントに一致します。
  • この場合、MongoDBは、返されるドキュメントがbでソートされることを保証できます。

上記のクエリのexplain()出力には、SORTステージはありません。また、explain()のあるクエリとないクエリのsort()出力は同じです。本質的に、sort()は無料で入手できます。

この主題を理解する価値のあるリソースは MongoDB複合インデックスの最適化 です。このブログ投稿は2012年に作成されたものであることに注意してください。一部の用語は古くなっている可能性がありますが、投稿の専門性は依然として重要です。

フォローアップ質問の更新

  1. MongoDBは ほとんどのクエリで1つのインデックスのみ を使用します。したがって、たとえば、クエリでメモリ内のSORTステージを回避するには

    _db.a.find({a:1}).sort({b:1})
    _

    インデックスは、abの両方のフィールドを同時にカバーする必要があります。例えば_{a:1,b:1}_などの複合インデックスが必要です。 2つの個別のインデックス_{a:1}_および_{b:1}_を使用することはできません。また、_{a:1}_インデックスが等値部分に使用され、_{b:1}_インデックスがソート部分に使用されることを期待してください。 。この場合、MongoDBは2つのインデックスのいずれかを選択します。

    したがって、結果はインデックスの順序で検索および返されるため、結果がソートされるのは正しいことです。

  2. 複合インデックスを使用したインメモリソートを回避するには、インデックスの最初の部分がクエリの等価部分に対応し、 2番目の部分は、クエリのソート部分に対応する必要があります(上記の(1)の説明に示すように)。

    次のようなクエリがある場合:

    _db.a.find({}).sort({a:1})
    _

    インデックス_{a:1,b:1}_は、並べ替え部分に使用できます(基本的にコレクション全体を返すため)。クエリが次のようになっている場合:

    _db.a.find({a:1}).sort({b:1})
    _

    同じインデックス_{a:1,b:1}_をクエリの両方の部分に使用することもできます。また:

    _db.a.find({a:1,b:1})
    _

    同じインデックスを使用することもできます_{a:1,b:1}_

    ここのパターンに注意してください。find()の後にsort()パラメータが続くのは、インデックスの順序_{a:1,b:1}_です。したがって、複合インデックスはequality-> sortの順に並べる必要があります。

さまざまなタイプのソートに関する更新

ドキュメント間でフィールドのタイプが異なる場合(たとえば、aが1つのドキュメントでは文字列、他のドキュメントでは数値、さらに別のドキュメントではブール値の場合)、並べ替えはどのように行われますか?

答えは MongoDB BSONタイプの比較順序 です。マニュアルページを言い換えると、順序は次のとおりです。

  1. MinKey(内部タイプ)
  2. ヌル
  3. 数値(int、long、double、decimals)
  4. 記号、文字列
  5. オブジェクト
  6. アレイ
  7. BinData
  8. ObjectId
  9. ブール
  10. 日付
  11. タイムスタンプ
  12. 正規表現
  13. MaxKey(内部タイプ)

したがって、上記の例から昇順を使用すると、数値を含むドキュメントが最初に表示され、次に文字列、次にブール値が表示されます。

30
kevinadi