Запрос выполняется быстрее без индекса

Ниже приведена упрощенная версия документа в моей базе данных:

{
    _id : 1,
    main_data : 100,
    sub_docs: [
        {
            _id : a,
            data : 22
        },
        {
            _id: b,
            data : 859
        },
        {
            _id: c,
            data: 151
        },

        ... snip ...

        {
           _id: m,
           data: 721
        },
        {
           _id: n,
           data: 111
        }
    ]
}

Итак, представьте, что у меня есть миллион таких документов с различными значениями данных (скажем, 0 - 1000). В настоящее время мой запрос выглядит примерно так:

db.myDb.find(
    { sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }
)

Также допустим, что приведенный выше запрос будет соответствовать только около 0,001% данных (таким образом, в общей сложности возвращается около 10 документов).

И у меня есть набор индексов с помощью:

db.myDb.ensureIndex( sub_docs.data )

Выполнение синхронизированного теста для этих данных, кажется, показывает, что это быстрее без какого-либо индекса, установленного на sub_docs.data.

Я использую Mongo 3.2.8.

Редактировать - Дополнительная информация:

Мой тест по времени - это Perl-скрипт, который запрашивает сервер, а затем извлекает соответствующие данные. Я запустил этот тест первым, когда у меня был включен индекс, однако медленное время запроса заставляло меня немного копать. Я хотел посмотреть, насколько плохим будет время запроса, если я уроню индекс, однако это улучшило время ответа на запрос! Я пошел немного дальше, я изобразил время ответа на запрос в сравнении с общим количеством документов в БД, оба графика показывают линейное увеличение времени запроса, но запрос с индексом увеличивается гораздо быстрее. Все время, пока я тестировал, я следил за использованием памяти сервера (которая невелика), поскольку моей первой мыслью было бы, что индекс не помещается в памяти.

Итак, в целом мой вопрос: почему для этого конкретного запроса этот запрос работает лучше без индекса и индекса? И есть ли способ улучшить скорость этого запроса с помощью лучшего индекса?

Обновить

Итак, прошло некоторое время, и я сузил его до индекса, не ограничивающего обе стороны параметров поиска запроса.

Приведенный выше запрос покажет границу индекса:

[-inf, 160]

Вместо 110-160. Я могу решить эту проблему с помощью функций index min и max следующим образом:

db.myDb.find(
    { sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }
).min({'subdocs.data': 110}).max({'subdocs.data': 160})

Однако (если возможно) я бы предпочел другой способ сделать это, так как я хотел бы использовать агрегатную функцию (которая, кажется, не поддерживает функции индекса min/max)

1 ответ

Решение

Хорошо, так что мне удалось разобрать это в конце. По какой-то причине индекс не ограничивает запрос, как я ожидал.

Запуск этого:

db.myDb.find({ sub_docs: { $elemMatch: { data: { $gte: 110, $lt: 160 } } } }).explain()

Ниже приведен фрагмент того, что делает индекс:

                      "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "sub_docs.data" : 1
                                },
                                "indexName" : "sub_docs.data_1",
                                "isMultiKey" : true,
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 1,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "sub_docs.data" : [
                                                "[-inf.0, 160.0)"
                                        ]
                                }
                        }

Вместо того, чтобы ограничить индекс между 110 и 160, он сканирует все документы, которые соответствуют ключу индекса, который меньше или равен 160. Я не включил его, но другим отклоненным планом было сканирование индекса от 110 до inf+. Вы можете решить эту проблему по минимальным / максимальным пределам, которые я упомянул выше в моем комментарии, однако это означает, что вы не можете использовать структуру агрегации, которая не работает.

Поэтому решение, которое я нашел, состояло в том, чтобы вытащить все данные, которые я хотел индексировать, в массив:

{
    _id : 1,
    main_data : 100,
    index_values : [
        22,
        859,
        151,

      ...snip...

        721,
        111
    ],
    sub_docs: [
        {
            _id : a,
            data : 22
        },
        {
            _id: b,
            data : 859
        },
        {
            _id: c,
            data: 151
        },

        ... snip ...

        {
           _id: m,
           data: 721
        },
        {
           _id: n,
           data: 111
        }
    ]
}

И тогда я создаю индекс:

db.myDb.ensureIndex({index_values : 1})

А затем спросите об этом:

db.myDb.find({ index_values : { $elemMatch: { $gte: 110, $lt: 160 } } }).explain()

Который производит:

"indexBounds" : {
       "index_values" : [
           "[110.0, 160.0]"
       ]
}

Так что гораздо меньше документов, чтобы проверить сейчас!

Другие вопросы по тегам