Как MongoDB выбирает планы-кандидаты

У меня медленный запрос в моем приложении. После создания двух индексов он использует их с большей производительностью в локальной БД. Но когда я развернул его в производственной БД, он по-прежнему использует исходный индекс.

Ниже этого то, что я сделал.

Свойства в коллекции tasks: team_id, project_id, created_by и assignee и т. д.

Запрос выглядит следующим образом

db.tasks.find({
  team_id: new ObjectId(teamId),
  $or: [
    {
      project_id: newObjectId(projectId),
      created_by: userId
    },
    {
      assignee: userId
    }
  ]
})

Изначально существует только один индекс для team_id, который будет проверять более 10 000 документов. Затем я добавил два новых индекса

project_1_created_by_1: {
  project: 1,
  created_by: 1
}

assignee_1: {
  assignee: 1
}

В локальной БД я выполнил свой запрос с помощью explain({ verbose: true }). Я вижу оцененные индексы MongoDB

[
  QueryOptimizerCursor: [
    'project_1_created_by_1',
    'assignee_1',
  ],
  BtreeCursor: 'team_1'
]

Наконец QueryOptimizerCursor победил.

Но когда я запустил его в производственной MongoDB, результат explain({ verbose: true }) показал, что он оценил только team_1 и BasicCursor.

[
  BtreeCursor: `team_1`,
  BasicCursor
]

Кто-нибудь дает мне какую-либо информацию, почему MongoDB не использовала новые созданные мной индексы, и, что еще хуже, не оценивала их.

PS: я могу подтвердить, что новые индексы были готовы в моей производственной базе данных, поскольку, когда я использую запрос db.tasks.find({project: xxx, created_by:yyy}).explain(), он использует новый, который я создал.

Обновлено

Версия производства MongoDB была 2.4.12, а локальная — 2.6.7. Когда я установил новую копию MongoDB 2.4.12 локально и выполнил тот же запрос, он использовал индекс team, а не QueryOptimizerCursor.

Не совсем уверен, что это только потому, что MongoDB 2.6.7 умнее, чем 2.4.12.


person Shaun Xu    schedule 18.07.2016    source источник


Ответы (1)


Если запрос может быть удовлетворен несколькими индексами, определенными в коллекции, MongoDB будет тестировать все применимые индексы параллельно. Планировщик запросов выберет первый индекс, который может вернуть 101 результат. Существуют и другие аспекты выбора индекса, но в целом это верно в соответствии с Оптимизация запросов.

Этот способ выбора индекса может выбрать субоптимальный индекс. Это связано с тем, что с точки зрения MongoDB у вас есть несколько индексов, описывающих одно и то же. Чтобы смягчить неоптимальный выбор индекса, который вы наблюдали, вы можете сделать:

  1. Удалите все остальные индексы, которые, по вашему мнению, неоптимальны.

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

  2. Используйте метод hint()

    hint() позволяет явно указать MongoDB использовать заданный индекс для запроса. Например:

    db.tasks.find(...).hint({project: 1, created_by: 1})
    

    См. https://docs.mongodb.com/v2.6/reference/operator/meta/hint/ для получения дополнительной информации о hint().

Еще один нюанс в вашем запросе заключается в том, что он включает оператор $or. В этом случае каждый термин в выражении $or должен иметь связанный с ним индекс, иначе MongoDB выполнит сканирование коллекции (BasicCursor в терминах MongoDB 2.6). Более подробно это объясняется на странице https://docs.mongodb.com/v2.6/reference/operator/query/or/#behaviors

person kevinadi    schedule 18.07.2016
comment
Спасибо за Ваш ответ. Я думаю, что мне нужно удалить индексы и найти лучший. Еще один вопрос: похоже, я могу указать только один индекс через hint(), но не могу указать несколько индексов, используемых моим оператором $or. - person Shaun Xu; 18.07.2016
comment
hint() допускает только один индекс. В большинстве случаев лучше удалить ненужные индексы, так как слишком большое количество индексов может замедлить вставку, поскольку каждая вставка, касающаяся термина индекса, будет означать, что соответствующий индекс также должен быть обновлен. - person kevinadi; 18.07.2016