web-dev-qa-db-ja.com

MongoDBでは、パフォーマンスに関して複合インデックスの順序はどのように重要ですか?

パラメータが照会されるのと同じ順序で複合インデックスを作成する必要があります。この順序はパフォーマンスに関してまったく重要ですか?

地球上のすべての人間のコレクションがあり、sex(99.9%の時間は「男性」または「女性」ですが、それでも文字列(バイナリではない))とname

特定のsexを持つ特定のnameのすべての人を選択できるようにする場合は、たとえば、 "John"という名前のすべての「男性」は、最初にsexまたはnameの複合インデックスを持つ方が良いですか?何故なの)?

26
Redsandro

レッドサンドロ、

Index CardinalityおよびSelectivity


1.インデックスのカーディナリティ

インデックスのカーディナリティは、フィールドに存在する可能性のある値の数を示します。フィールドsexには、2つの値しかありません。カーディナリティが非常に低いnames, usernames, phone numbers, emailsなどの他のフィールドは、コレクション内のすべてのドキュメントに対してより一意の値を持ちます。これは高いカーディナリティと見なされます。

  • より大きなカーディナリティ

    フィールドのカーディナリティが大きいほど、インデックスはより役立ちます。インデックスは検索スペースを狭め、それをはるかに小さなセットにするためです。

    sexにインデックスがあり、Johnという名前の男性を探しているとします。最初にsexでインデックスを作成した場合にのみ、結果スペースを約%50だけ絞り込みます。逆に、nameでインデックスを作成した場合は、結果セットをJohnという名前のユーザーのごく一部に絞り込み、それらのドキュメントを参照して性別を確認します。

  • 経験則

    high-cardinalityキーにインデックスを作成するか、複合インデックスの最初にhigh-cardinalityキーを配置してください。詳細については、本の複合インデックスに関するセクションをご覧ください。

    MongoDB The Definitive Guide


2.選択性

また、インデックス 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つのフィールドの複合インデックスの例を示しています。

enter image description here

複合インデックスを作成すると、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}を使用すると、複合インデックスをより適切に使用できます。このトピックについて読むことができるものはもっとたくさんあります。これがいくらか明確になることを願っています。

75

これについて自分で実験を行ったところ、最初に不十分に区別されたインデックスキーを使用してもパフォーマンスが低下することはないようです。 (私は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を使用すると、インデックスの最初のフィールドでの差別化が不十分であってもパフォーマンスが低下しないことを示唆しています。

1
user3413723