MongoDB не использует мой индекс

У меня есть коллекция журналов с миллионами записей. Создание нового индекса занимает «вечность». Поэтому было бы предпочтительнее использовать существующие индексы.

Теперь я хочу получить количество вхождений определенных кодов ошибок. Я использую этот запрос, и функционально он отлично работает:

db.getCollection('logs.res').aggregate([
    {
       $match:{    
           timeStamp: {
               $gte: new Date('2017-05-01').getTime(), // timeStamp is Number
               $lt : new Date('2017-05-02').getTime()  // of ms since epoch
           },
           '[email protected]': {
               $ne: null
           }
        }
    },
    {
        $group: {
            _id: '[email protected]',
            count: {$sum: 1}
        }
    },
    {
        $sort: { count: -1}
    }
]);

Проблема в том, что для выполнения этого в течение дня требуется около 10 секунд. Я предполагал, что будет использоваться следующий индекс: [email protected]_1:

{
    "timeStamp" : -1,
    "[email protected]" : 1
}

Тем не менее, MongoDB, похоже, непреклонно использует некоторый индекс timeStamp: 1 (с некоторыми другими индексами, не связанными с запросом) и сканирует все результаты, чтобы увидеть, могут ли некоторые ответы иметь прикрепленный ErrorCode, даже если эта информация должна быть в индексе.

Вот explain():

введите здесь описание изображения

  • Есть ли способ использовать [email protected]_1 индекс, чтобы ускорить это?
  • Почему не используется этот индекс? Вероятно, я неправильно понимаю, как индексы используются в этом запросе.

Запуск MongoDB 3.2.7 на OSX.

примечание: я также пробовал $empty: true вместо $ne: null. Он дает те же результаты, но некоторые говорят, что вы не можете использовать $empty, если хотите использовать составной индекс. Однако многие вопросы о переполнении стека устарели (mongo 2.x).


person Redsandro    schedule 20.09.2017    source источник


Ответы (2)


Обычные индексы mongodb используют как значение поля, так и тип для построения дерева.

Такие запросы, как $empty: true или $ne: null, не имеют параметров какого-либо типа и не могут использовать такие индексы. Это особый случай, для которого требуется специальный разреженный индекс.

Если ваш индекс [email protected]_1 создан как:

db.getCollection('logs.res').createIndex(
    {
        "timeStamp" : -1,
        "[email protected]" : 1
    },
    { sparse: true }
)

Он должен наилучшим образом поддерживать ваш запрос. В остальном между [email protected]_1 и timeStamp_1_module_1_etc нет большой разницы, поскольку используется только первое поле.

person Alex Blex    schedule 20.09.2017
comment
Это может быть актуально. Могу ли я вместо этого сделать $nin: [array list of all error ids]? Или неразреженный индекс для поля, которое не всегда установлено, практически бесполезен? - person Redsandro; 20.09.2017
comment
Или еще лучше: $ne: 999999. Это параметр типа number. Он просто не используется. - person Redsandro; 20.09.2017
comment
Да, оба должны использовать индекс, но это не тот же запрос, что и $ne: null. Вы получите нули в обоих случаях. Если вы знаете все коды ошибок, вы можете сделать $in: [the list], но вы можете пропустить документы с неожиданными кодами ошибок. - person Alex Blex; 21.09.2017

Выигрышный план: CACHED PLAN. Вы можете попробовать очистить план кэширования.

db.getCollection('logs.res').getPlanCache().clear()

Если после очистки кеша Mongo по-прежнему использует неправильный индекс. Вы можете попробовать установить план запроса или использовать «подсказку», чтобы заставить ваш индекс

person Marco    schedule 20.09.2017
comment
Насколько мне известно, хинтинг предназначен для целей тестирования, и вы не можете установить хинт индекса в конвейере агрегации. - person Redsandro; 20.09.2017
comment
Вы можете использовать функцию planCacheSetFilter перед агрегированием - person Marco; 21.09.2017