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)。これは、インデックスなしでソートするのと同じです。
私は(多分私は間違っていると思いますが)すべての並べ替えはインデックススキャンを必要としないと思います。
更新:
ケビンの答えと提供されたリンクは質問をかなり絞り込みますが、いくつかの点を確認/明確化したいと思います。
並べ替えはどのように機能しますか$in
または$or
クエリ?たとえば、クエリが
{a: {$in: [4, 6, 2, 1, 3, 10]}, b: {$gt: 1, $lt: 6}}
... a
とb
には、この順序で複合インデックスがあります。並べ替えがa
またはb
の場合、並べ替えはどのように機能しますか? $or
はさらに複雑です。私が理解しているように、$or
クエリは基本的に複数の個別のクエリに分割されます。 $or
クエリは、少なくとも個別のクエリの結果をマージするために、常にインメモリソートですか?
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
を持つドキュメントに一致します。b
でソートされることを保証できます。上記のクエリのexplain()
出力には、SORT
ステージはありません。また、explain()
のあるクエリとないクエリのsort()
出力は同じです。本質的に、sort()
は無料で入手できます。
この主題を理解する価値のあるリソースは MongoDB複合インデックスの最適化 です。このブログ投稿は2012年に作成されたものであることに注意してください。一部の用語は古くなっている可能性がありますが、投稿の専門性は依然として重要です。
フォローアップ質問の更新
MongoDBは ほとんどのクエリで1つのインデックスのみ を使用します。したがって、たとえば、クエリでメモリ内のSORT
ステージを回避するには
_db.a.find({a:1}).sort({b:1})
_
インデックスは、a
とb
の両方のフィールドを同時にカバーする必要があります。例えば_{a:1,b:1}
_などの複合インデックスが必要です。 2つの個別のインデックス_{a:1}
_および_{b:1}
_を使用することはできません。また、_{a:1}
_インデックスが等値部分に使用され、_{b:1}
_インデックスがソート部分に使用されることを期待してください。 。この場合、MongoDBは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タイプの比較順序 です。マニュアルページを言い換えると、順序は次のとおりです。
したがって、上記の例から昇順を使用すると、数値を含むドキュメントが最初に表示され、次に文字列、次にブール値が表示されます。