Mongodb агрегат $ групповой этап занимает много времени - PullRequest
0 голосов
/ 25 октября 2019

Я практикую, как использовать MongoDB агрегацию , но они, кажется, занимают очень много времени (время выполнения).

Проблема , кажется, возникаетвсякий раз, когда я использую $group. Все остальные запросы выполняются просто отлично.

У меня есть 1.3 миллион фиктивных документов , которые должны выполнить две основные операции : получить count из IP-адресов и уникальных IP-адресов.

Моя схема выглядит примерно так:

{
    "_id":"5da51af103eb566faee6b8b4",
    "ip_address":"...",
    "country":"CL",
    "browser":{
        "user_agent":...",
    }
}

Запуск базового $groupзапрос занимает в среднем около 12 с, что слишком медленно.

Я провел небольшое исследование, и кто-то предложил создать index для ip_addresses. Кажется, это замедлило процесс, потому что запросы теперь занимают 13-15 с.

Я использую MongoDB и запрос Я работаю выглядит так:

    visitorsModel.aggregate([
        {
            '$group': {
                '_id': '$ip_address',
                'count': {
                    '$sum': 1
                }
            }
        }
    ]).allowDiskUse(true)
        .exec(function (err, docs) {
            if (err) throw err;

            return res.send({
                uniqueCount: docs.length
            })
        })

Любая помощь приветствуется.

Редактировать: Я забыл упомянуть, кто-то предположил, что это может быть аппаратная проблема? Я выполняю запрос на ноутбуке с оперативной памятью i5, 8 ГБ, если это помогает.

Редактировать 2 : План запроса:

{
    "stages" : [
        {
            "$cursor" : {
                "query" : {

                },
                "fields" : {
                    "ip_address" : 1,
                    "_id" : 0
                },
                "queryPlanner" : {
                    "plannerVersion" : 1,
                    "namespace" : "metrics.visitors",
                    "indexFilterSet" : false,
                    "parsedQuery" : {

                    },
                    "winningPlan" : {
                        "stage" : "COLLSCAN",
                        "direction" : "forward"
                    },
                    "rejectedPlans" : [ ]
                },
                "executionStats" : {
                    "executionSuccess" : true,
                    "nReturned" : 1387324,
                    "executionTimeMillis" : 7671,
                    "totalKeysExamined" : 0,
                    "totalDocsExamined" : 1387324,
                    "executionStages" : {
                        "stage" : "COLLSCAN",
                        "nReturned" : 1387324,
                        "executionTimeMillisEstimate" : 9,
                        "works" : 1387326,
                        "advanced" : 1387324,
                        "needTime" : 1,
                        "needYield" : 0,
                        "saveState" : 10930,
                        "restoreState" : 10930,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "direction" : "forward",
                        "docsExamined" : 1387324
                    }
                }
            }
        },
        {
            "$group" : {
                "_id" : "$ip_address",
                "count" : {
                    "$sum" : {
                        "$const" : 1
                    }
                }
            }
        }
    ],
    "ok" : 1
}


Ответы [ 2 ]

0 голосов
/ 25 октября 2019

Это некоторая информация об использовании $group этапа агрегации, если он использует индексы, его ограничения и что можно попытаться преодолеть.

1. Этап $ group не использует индекс : Mongodb Агрегация: использует ли $ group индекс?


2. $ group Оператор и память :

Этап $group имеет ограничение в 100 мегабайт оперативной памяти. По умолчанию, если уровень превышает этот предел, $group возвращает ошибку. Чтобы разрешить обработку больших наборов данных, установите для параметра allowDiskUse значение true. Этот флаг позволяет операциям $ group записывать во временные файлы.

См. Документы MongoDb для $ group Оператор и память


3. Пример с использованием $ group и Count :

Коллекция называется cities:

{ "_id" : 1, "city" : "Bangalore", "country" : "India" }
{ "_id" : 2, "city" : "New York", "country" : "United States" }
{ "_id" : 3, "city" : "Canberra", "country" : "Australia" }
{ "_id" : 4, "city" : "Hyderabad", "country" : "India" }
{ "_id" : 5, "city" : "Chicago", "country" : "United States" }
{ "_id" : 6, "city" : "Amritsar", "country" : "India" }
{ "_id" : 7, "city" : "Ankara", "country" : "Turkey" }
{ "_id" : 8, "city" : "Sydney", "country" : "Australia" }
{ "_id" : 9, "city" : "Srinagar", "country" : "India" }
{ "_id" : 10, "city" : "San Francisco", "country" : "United States" }

Запрос коллекция для подсчета городов в каждой стране:

db.cities.aggregate( [
    { $group: { _id: "$country", cityCount: { $sum: 1 } } },
    { $project: { country: "$_id", _id: 0, cityCount: 1 } }
] )

Результат :

{ "cityCount" : 3, "country" : "United States" }
{ "cityCount" : 1, "country" : "Turkey" }
{ "cityCount" : 2, "country" : "Australia" }
{ "cityCount" : 4, "country" : "India" }


4. Использование параметра allowDiskUse :

db.cities.aggregate( [
    { $group: { _id: "$country", cityCount: { $sum: 1 } } },
    { $project: { country: "$_id", _id: 0, cityCount: 1 } }
],  { allowDiskUse : true } )

Обратите внимание, что в этом случае нет никакой разницы в производительности или выводе запроса. Это показывает только использование.


5. Некоторые варианты, чтобы попробовать (предложения) :

Вы можете попробовать несколько вещей, чтобы получить некоторый результат (только для ознакомительных целей):

  • Использовать $limit stage и ограничитьколичество обработанных документов и посмотреть, каков результат. Например, вы можете попробовать { $limit: 1000 }. Обратите внимание, что этот этап должен предшествовать этапу $group.
  • Вы также можете использовать этапы $match, $project перед этапом $group для управления shape и size входа. Это может вернуть результат (вместо ошибки).



[РЕДАКТИРОВАТЬ ДОБАВИТЬ]

Примечания о различных иКоличество:

Использование той же коллекции cities - чтобы получить уникальные страны и их количество, вы можете попробовать использовать стадию агрегирования $count вместе с $group, как в следующих двух запросах.

Distinct:

db.cities.aggregate( [
   { $match: { country: { $exists: true } } },
   { $group: { _id: "$country" } },
   { $project: { country: "$_id", _id: 0 } }
] )

Результат:

{ "country" : "United States" }
{ "country" : "Turkey" }
{ "country" : "India" }
{ "country" : "Australia" }

Чтобы получить вышеуказанный результат в виде одного документа с массивом уникальных значений, используйте оператор $addToSet:

db.cities.aggregate( [
   { $match: { country: { $exists: true } } },
   { $group: { _id: null, uniqueCountries: { $addToSet:  "$country" } } },
   { $project: { _id: 0 } },
] )

Результат: { "uniqueCountries" : [ "United States", "Turkey", "India", "Australia" ] }

Количество:

db.cities.aggregate( [
   { $match: { country: { $exists: true } } },
   { $group: { _id: "$country" } },
   { $project: { country: "$_id", _id: 0 } },
   { $count: "uniqueCountryCount" }
] )

Результат: { "uniqueCountryCount" : 4 }

В вышеупомянутых запросах этап $match используется для фильтрации любых документов с несуществующим или нулевым полем country. Этап $project изменяет форму документа (ов).

Язык запросов MongoDB:

Обратите внимание, что два запроса получают схожие результаты при использовании запроса MongoDBкоманды language : db.collection.distinct("country") и db.cities.distinct("country").length (обратите внимание, distinct возвращает массив).

0 голосов
/ 25 октября 2019

Вы можете создать индекс

db.collectionname.createIndex( { ip_address: "text" } )

Попробуйте, это быстрее. Я думаю, что это поможет вам.

...