パラメータが照会されるのと同じ順序で複合インデックスを作成する必要があります。この順序はパフォーマンスに関してまったく重要ですか?
地球上のすべての人間のコレクションがあり、sex
(99.9%の時間は「男性」または「女性」ですが、それでも文字列(バイナリではない))とname
。
特定のsex
を持つ特定のname
のすべての人を選択できるようにする場合は、たとえば、 "John"という名前のすべての「男性」は、最初にsex
またはname
の複合インデックスを持つ方が良いですか?何故なの)?
レッドサンドロ、
Index Cardinality
およびSelectivity
。
インデックスのカーディナリティは、フィールドに存在する可能性のある値の数を示します。フィールドsex
には、2つの値しかありません。カーディナリティが非常に低い。 names, usernames, phone numbers, emails
などの他のフィールドは、コレクション内のすべてのドキュメントに対してより一意の値を持ちます。これは高いカーディナリティと見なされます。
フィールドのカーディナリティが大きいほど、インデックスはより役立ちます。インデックスは検索スペースを狭め、それをはるかに小さなセットにするためです。
sex
にインデックスがあり、Johnという名前の男性を探しているとします。最初にsex
でインデックスを作成した場合にのみ、結果スペースを約%50だけ絞り込みます。逆に、name
でインデックスを作成した場合は、結果セットをJohnという名前のユーザーのごく一部に絞り込み、それらのドキュメントを参照して性別を確認します。
high-cardinality
キーにインデックスを作成するか、複合インデックスの最初にhigh-cardinality
キーを配置してください。詳細については、本の複合インデックスに関するセクションをご覧ください。
また、インデックス selectively を使用して、インデックス付きフィールドで可能なドキュメントの数を制限するクエリを記述します。単純にするために、次のコレクションを検討してください。インデックスが{name:1}
の場合、クエリを実行した場合{ name: "John", sex: "male"}
。 1
ドキュメントをスキャンする必要があります。 MongoDBが選択的であることを許可したからです。
{_id:ObjectId(),name:"John",sex:"male"}
{_id:ObjectId(),name:"Rich",sex:"male"}
{_id:ObjectId(),name:"Mose",sex:"male"}
{_id:ObjectId(),name:"Sami",sex:"male"}
{_id:ObjectId(),name:"Cari",sex:"female"}
{_id:ObjectId(),name:"Mary",sex:"female"}
次のコレクションを検討してください。インデックスが{sex:1}
の場合、クエリを実行した場合{sex: "male", name: "John"}
。 4
ドキュメントをスキャンする必要があります。
{_id:ObjectId(),name:"John",sex:"male"}
{_id:ObjectId(),name:"Rich",sex:"male"}
{_id:ObjectId(),name:"Mose",sex:"male"}
{_id:ObjectId(),name:"Sami",sex:"male"}
{_id:ObjectId(),name:"Cari",sex:"female"}
{_id:ObjectId(),name:"Mary",sex:"female"}
より大きなデータセットで起こり得る違いを想像してみてください。
複合インデックスについて誤った仮定をするのは簡単です。 複合インデックスに関するMongoDBドキュメント によると。
MongoDBは複合インデックスをサポートし、単一のインデックス構造が複数のフィールドへの参照を保持しますコレクションのドキュメント内。次の図は、2つのフィールドの複合インデックスの例を示しています。
複合インデックスを作成すると、1 Indexは複数のフィールドを保持します。したがって、{"sex" : 1, "name" : 1}
でコレクションにインデックスを付けると、インデックスはおおよそ次のようになります。
["male","Rick"] -> 0x0c965148
["male","John"] -> 0x0c965149
["male","Sean"] -> 0x0cdf7859
["male","Bro"] ->> 0x0cdf7859
...
["female","Kate"] -> 0x0c965134
["female","Katy"] -> 0x0c965126
["female","Naji"] -> 0x0c965183
["female","Joan"] -> 0x0c965191
["female","Sara"] -> 0x0c965103
コレクションを{"name" : 1, "sex" : 1}
でインデックス付けすると、インデックスはおおよそ次のようになります。
["John","male"] -> 0x0c965148
["John","female"] -> 0x0c965149
["John","male"] -> 0x0cdf7859
["Rick","male"] -> 0x0cdf7859
...
["Kate","female"] -> 0x0c965134
["Katy","female"] -> 0x0c965126
["Naji","female"] -> 0x0c965183
["Joan","female"] -> 0x0c965191
["Sara","female"] -> 0x0c965103
Prefix として{name:1}
を使用すると、複合インデックスをより適切に使用できます。このトピックについて読むことができるものはもっとたくさんあります。これがいくらか明確になることを願っています。
これについて自分で実験を行ったところ、最初に不十分に区別されたインデックスキーを使用してもパフォーマンスが低下することはないようです。 (私はwiredtigerでmongodb 3.4を使用していますが、mmapとは異なる場合があります)。 2億5,000万のドキュメントをitems
という新しいコレクションに挿入しました。各ドキュメントは次のようになりました:
_{
field1:"bob",
field2:i + "",
field3:i + ""
_
_"field1"
_は常に_"bob"
_と同じでした。 _"field2"
_はi
と等しいため、完全に一意でした。最初にfield2を検索しましたが、2億5000万のドキュメントをスキャンするのに1分以上かかりました。次に、次のようなインデックスを作成しました。
_`db.items.createIndex({field1:1,field2:1})`
_
もちろん、field1はすべてのドキュメントで「ボブ」であるため、目的のドキュメントを見つける前に、インデックスで多くのアイテムを検索する必要があります。しかし、これは私が得た結果ではありませんでした。
インデックスの作成が完了した後、コレクションをもう一度検索しました。今回は以下の結果を得ました。 _"totalKeysExamined"
_が毎回1であることがわかります。したがって、おそらくワイヤードタイガーか何かで、彼らはこれをより良くする方法を見つけました。私はwiredtigerが実際にインデックスのプレフィックスを圧縮するのを読んだので、それはそれと関係があるかもしれません。
db.items.find({field1:"bob",field2:"250888000"}).explain("executionStats")
_{
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 4,
"totalKeysExamined" : 1,
"totalDocsExamined" : 1,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 1,
"executionTimeMillisEstimate" : 0,
"works" : 2,
"advanced" : 1,
...
"docsExamined" : 1,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1,
"executionTimeMillisEstimate" : 0,
...
"indexName" : "field1_1_field2_1",
"isMultiKey" : false,
...
"indexBounds" : {
"field1" : [
"[\"bob\", \"bob\"]"
],
"field2" : [
"[\"250888000\", \"250888000\"]"
]
},
"keysExamined" : 1,
"seeks" : 1
}
}
_
次に、_field3
_にインデックスを作成しました(フィールド2と同じ値です)。それから私は検索しました:
db.items.find({field3: "250888000"});
複合インデックスの場合と同じ4ミリ秒かかりました。これをfield2とfield3の異なる値で何度も繰り返し、毎回わずかな違いがありました。これは、wiredtigerを使用すると、インデックスの最初のフィールドでの差別化が不十分であってもパフォーマンスが低下しないことを示唆しています。