Вопрос:

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

mongodb mongodb-query mongodb-indexes

248 просмотра

1 ответ

68 Репутация автора

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

{
    _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)

Автор: Richard Jackson Источник Размещён: 22.08.2016 08:29

Ответы (1)


1 плюс

68 Репутация автора

Решение

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

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

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]"
       ]
}

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

Автор: Richard Jackson Размещён: 15.09.2016 09:19
Вопросы из категории :
32x32