Понимание производительности: агрегация монго против подсчета

Если я выполняю запрос на подсчет, я получаю результаты за ‹2 секунды.

db.coll.find({"A":1,"createDate":{"$gt":new Date("2011-05-21"),"$lt":new Date("2013-08-21")}}).count()

При этом используется следующий индекс

db.coll.ensureIndex({"A":1,"createDate":1})

Точно так же есть 4 столбца A, B, C, D (значения всегда равны 0 или 1), для которых я запускаю 4 запроса на подсчет и получаю результаты за ‹10 секунд.

Я просмотрел документацию по структуре агрегации и создал агрегированный запрос, чтобы выполнить все 4 суммы вместе.

db.coll.aggregate(  { $match : {"createDate":{$gt:new Date("2013-05-21"),$lt:new Date("2013-08-21")} } },
{ $group :
                         { _id:null,
                         totalA : { $sum : "$A" },
                         totalB : {$sum: "$B},
                         totalC:{$sum: "$C"},
                         totalD:{$sum: "$D"}}} 
 ) 

Я также создал индекс:

db.coll..ensureIndex({"createDate":1,"A":1,"B":1,"C":1,"D":1})

Согласно документации, этот индекс охватывает мою агрегатную функцию. Но возврат агрегата происходит через ~ 18 секунд.

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


person crazydiv    schedule 21.02.2014    source источник


Ответы (2)


Во-первых, хотя это и не задокументировано для версии 2.4.8, вы можете запустить объяснение, используя вызов db.runCommand:

db.runCommand({
    aggregate: "coll",
    pipeline: [      
        { $match : 
            {"createDate":{$gt:new Date("2013-05-21"),$lt:new Date("2013-08-21")} } 
        },
        { $group : { 
              _id:null,
              totalA: {$sum :"$A"},
              totalB: {$sum: "$B"},
              totalC: {$sum: "$C"},
              totalD: {$sum: "$D"}
        }} 
    ],
    explain: true
})

Что даст вам некоторое представление о том, что происходит.

Кроме того, в первую очередь, вы сравниваете яблоки с апельсинами.

Когда вы вводите count() в запросе, он использует свойства результата курсора, чтобы получить количество совпавших документов.

При объединении вы выбираете расширенное совпадение, а затем объединяете все эти результаты в сумму всех элементов. Если ваш первоначальный $match имеет много результатов, тогда все они должны быть обработаны вместе с $sum.

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

person Neil Lunn    schedule 21.02.2014

Краткий ответ: в вашем случае агрегация выполняется медленнее, потому что она требует большей обработки данных, в то время как использование индекса монго может эффективно вычислять количество. Агрегация предназначена для вычисления некоторых сложных результатов (grouping и т. д.), а для простого подсчета достаточно count().

Причина этого в том, что агрегация в mongodb представляет собой структуру для агрегации данных и основана на концепции конвейеров обработки данных. Mongo логически передает всю коллекцию в конвейер агрегации. По этой причине отсутствует explain для агрегации в целом (на момент написания статьи версия 2.4). Это означает, что в основном используется один метод доступа, а остальное время используется для обработки. Но, похоже, есть поддержка explain в последние версии.

Вы можете выполнить раннюю фильтрацию, в которой используется подмножество данных в коллекции.

Ранняя фильтрация

Если для вашей операции агрегирования требуется только подмножество данных в коллекции, используйте этапы $match, $limit и $skip, чтобы ограничить документы, поступающие в начале конвейера. При размещении в начале конвейера операции $match используют подходящие индексы для сканирования только совпадающих документов в коллекции.

Размещение этапа конвейера $match, за которым следует этап $sort в начале конвейера, логически эквивалентно одному запросу с сортировкой и может использовать индекс. По возможности размещайте операторы $match в начале конвейера.

Поведение конвейера агрегации.

person Community    schedule 21.02.2014
comment
есть объяснение для агрегации, просто добавьте аргумент {explain:true} после массива конвейеров. И всю коллекцию не нужно передавать, если есть индекс, который может предоставить необходимые значения. Проблема здесь в том, что агрегация делает что-то отличное от подсчета, она фактически добавляет значения этих полей, не считая, сколько из них ненулевые. - person Asya Kamsky; 23.02.2014
comment
Он еще не выпущен, но появится в версии 2.6. - person ; 25.02.2014
comment
неверно, он доступен в текущей версии 2.4.x и существует уже несколько месяцев. - person Asya Kamsky; 28.02.2014
comment
Кажется, это новое в версии 2.5.3:docs.mongodb.org/master/reference/method/ - person ; 28.02.2014
comment
работает в 2.4.6,2.4.7,2.4.8,2.4.9. Я полагаю, что предоставленная вами ссылка показывает, что, начиная с версии 2.5.2, требуется, чтобы конвейер представлял собой массив для правильного анализа документа опций. - person Asya Kamsky; 28.02.2014