Есть ли способ сделать несколько эффективных (с использованием индекса) находок в одном запросе? - PullRequest
0 голосов
/ 13 апреля 2019

У меня есть коллекция mongodb «событий», которые произошли в мире, где «событие» - это изменение значения некоторой метрики, которое заставляет нас присвоить ей другой статус. Например, если widgetCount падает ниже 10, мы можем создать событие «предупреждение», когда это произойдет, а затем, если оно поднимется выше 10, мы запишем событие «ОК». Запись о событии содержит информацию о том, где и когда произошло событие, и какой показатель изменился, чтобы вызвать событие. Моему приложению узла часто нужно знать самое последнее событие для каждой метрики в данном месте, и может быть около 15 метрик, которые меня интересуют одновременно. У меня возникают трудности с получением запроса (или запросов) для получения этой информации, чтобы она работала хорошо, несмотря на наличие правильных индексов.

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

{
  location: 'Anytown',
  metric: 'widgetCount',
  time: new Date('2019-01-01 00:00:00'),
  value: 42,
  status: 'ok'
}

И у коллекции есть индекс на {location: 1, metric: 1, time: -1}, а также на {location: 1, time: -1}.

Когда я find() каждая комбинация местоположения / метрики индивидуально от узла, каждый запрос полностью соответствует первому индексу, и для выполнения в монго требуется всего 1-5 мс (я знаю из проверки профилирования в журнале монго), поэтому возможно, 40 мс для всех показателей в месте. Однако непроизводительные затраты на выполнение 15 различных запросов увеличивают общее время извлечения в узле для всех метрик в местоположении до примерно 3 с на местоположение, даже если они распараллелены в Promise.all(). Отдельные запросы выглядят так (в данном случае я ищу положение дел около 1 апреля в полночь, а не прямо сейчас):

// Repeat this 15 times in a Promise.all(), once for each metric of interest
db.getCollection('events')
.find({
  location: 'Anytown',
  metric: 'widgetCount',
  time: {$lt: new Date('2019-04-01 00:00:00')}
})
.sort({time: -1})
.limit(1)
.toArray()

Я понял, что, поскольку у всех находок есть некоторые общие условия (местоположение и временной интервал), я мог бы использовать конвейер агрегации, сначала сопоставляя по общим критериям, сортируя по времени, а затем выполняя фасеты, где совпадает каждый фасет к отдельной метрике. Это несколько улучшает общую производительность, сокращая пятнадцать запросов до одного - общее время поиска в узле для местоположения составляет около 1,5 с при использовании этого подхода. Однако время, проведенное в монго, увеличивается на порядок - примерно до 400 мс - поскольку индекс используется только для начального шага $match, тогда вторичный $match в каждом фасете должен быть удовлетворен сканирование строк. Фасетный подход выглядит так:

db.getCollection('events').aggregate([
  {$match: {location: 'Anytown', time: {$lt: new Date('2019-04-01 00:00:00')}},
  {$sort: {time: -1}},
  {$facet: {
    widgetCount: [{$match: {metric: 'widgetCount'}}, {limit: 1}],
    // ...
    // repeat for each different metric I'm interested in
    // ...
  }}
]).toArray()

Что мне хотелось бы, так это каким-то образом сказать Монго: «сделайте эти 15 различных находок в одной коллекции одновременно, а затем верните мне все результаты в одном объекте (или массиве)». В мире моей мечты я мог получить низкие индивидуальные запросы времени моего первого подхода в сочетании с низкими издержками запроса моего второго подхода. Есть ли способ сделать это? В качестве альтернативы, есть ли способы уменьшить накладные расходы на запрос, чтобы первый подход с 15 отдельными запросами работал лучше?

Примечание: я также попытался сделать вариант подхода $facet, где $facet - это первая стадия в конвейере, а $match каждого аспекта предоставляет все критерии запроса, в надежде, что Монго может использовать индекс внутри каждый фасет, если фасет $match был первой встреченной инструкцией, но это оказалось намного медленнее, чем любой из вышеперечисленных подходов, потому что это было сделано полностью с помощью сканирования строк. tl; dr: Очевидно, что mongo не будет использовать индексы внутри фасета ни при каких обстоятельствах.

В настоящее время я нахожусь на mongodb 3.4 и узле 10.14.1, чего бы это ни стоило.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...