Неоправданно медленный запрос MongoDB, хотя запрос прост и согласован с индексами

Я запускаю сервер MongoDB (который буквально все работает). Сервер имеет 64 ГБ оперативной памяти и 16 ядер, плюс 2 ТБ на жестком диске для работы.

Структура документа

В базе данных имеется коллекция domains с около 20 миллионами документов. В каждом документе есть приличный объем данных, но для наших целей документ структурирован так:

{
    _id: "abcxyz.com",
    LastUpdated: <date>,
    ...
}

Поле _id - это имя домена, на которое ссылается документ. На LastUpdated есть восходящий индекс. LastUpdated обновляется сотнями тысяч записей в день. В основном каждый раз, когда новые документы становятся доступными для документа, документ обновляется, а поле LastUpdated обновляется до текущей даты/времени.

Запрос

У меня есть механизм, который извлекает данные из базы данных, поэтому его можно индексировать в индексе Lucene. Поле LastUpdated является ключевым драйвером для пометки изменений, внесенных в документ. Для поиска документов, которые были изменены, и страницы через эти документы, я делаю следующее:

{
    LastUpdated: { $gte: ISODate(<firstdate>), $lt: ISODate(<lastdate>) },
    _id: { $gt: <last_id_from_previous_page> }
}

sort: { $_id:1 }

Когда документы не возвращаются, даты начала и окончания перемещаются вперед, а поле _id "anchor" - reset. Эта настройка толерантна к документам с предыдущих страниц, у которых было изменено их значение LastUpdated, т.е. Пейджинг не будет неправильно смещен количеством документов на предыдущих страницах, которые теперь технически уже не находятся на этих страницах.

Проблема

Я хочу в идеале выбрать около 25000 документов за раз, но по какой-то причине сам запрос (даже при выборе только 500 документов) очень медленный.

Я выполнил запрос:

db.domains.find({
    "LastUpdated" : {
        "$gte" : ISODate("2011-11-22T15:01:54.851Z"),
        "$lt" : ISODate("2011-11-22T17:39:48.013Z")
    },
    "_id" : { "$gt" : "1300broadband.com" }
}).sort({ _id:1 }).limit(50).explain()

Это так медленно, что объяснение (на момент написания этого) работает более 10 минут и еще не завершено. Я буду обновлять этот вопрос, если он когда-либо закончится, но, конечно, дело в том, что запрос ЧРЕЗВЫЧАЙНО медленный.

Что я могу сделать? Я не имею ни малейшего понятия, что проблема может быть с запросом.

ИЗМЕНИТЬ Объяснение закончилось через 55 минут. Вот он:

{
    "cursor" : "BtreeCursor Lastupdated_-1__id_1",
    "nscanned" : 13112,
    "nscannedObjects" : 13100,
    "n" : 50,
    "scanAndOrder" : true,
    "millis" : 3347845,
    "nYields" : 5454,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {
            "LastUpdated" : [
                    [
                            ISODate("2011-11-22T17:39:48.013Z"),
                            ISODate("2011-11-22T15:01:54.851Z")
                    ]
            ],
            "_id" : [
                    [
                            "1300broadband.com",
                            {

                            }
                    ]
            ]
    }
}

Ответ 1

Включение в очень похожую проблему, и Рекомендации по индексированию и часто задаваемые вопросы на Mongodb.org говорит, цитируйте:

Запрос диапазона также должен быть последним столбцом в индексе

Итак, если у вас есть ключи a, b и c и запустите db.ensureIndex({a: 1, b: 1, c: 1}), это "рекомендации", чтобы максимально использовать индекс

Хорошо:

  • находка (а = 1, Ь > 2)

  • найти (a > 1 и a < 10)

  • найти (a > 1 и a < 10).sort(a)

Плохо:

  • find (a > 1, b = 2)

Используйте только запрос диапазона или сортировку по одному столбцу. Хорошо:

  • найти (а = 1, B = 2).sort(с)

  • находка (а = 1, Ь > 2)

  • найти (a = 1, b > 2 и b < 4)

  • находка (а = 1, Ь > 2).sort(б)

Плохо:

  • найти (a > 1, Ь > 2)

  • находка (а = 1, Ь > 2).sort(с)

Надеюсь, что это поможет!

/J

Ответ 2

Хорошо, я решил. Преступник был "scanAndOrder": true, который предположил, что индекс не использовался по назначению. Правильный составной индекс сначала содержит первичное поле сортировки, а затем запрашиваемые поля.

{ "_id":1, "LastUpdated":1 }

Ответ 3

Вы пытались добавить _id в свой составной индекс. Поскольку вы используете его как часть запроса, ему все равно придется выполнять полное сканирование таблицы?