Медленный запрос в MongoDB с совокупным запросом на всю коллекцию с использованием группы

У меня проблемы с производительностью API, который я разрабатываю с помощью NodeJS+Express+MongoDB.

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

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

Требование: Общее количество уникальных экспортеров из каждой страны вместе с любыми 3 записями из каждой страны.

При запуске explain() на моем aggregate function я получаю следующие ключевые указатели, которые сигнализируют о том, что мои запросы медленные. Пожалуйста, поправьте меня, если я ошибаюсь.

  1. "indexFilterSet": false
  2. "winningPlan": { "stage": "COLLSCAN", "direction": "forward" },

Выполнил запрос по 9,264,947 записям, и затраченное время составляет около 32 seconds. Я пытался использовать составной индекс, а также индекс с одним полем, но это совсем не помогает, так как я чувствую, что индекс не используется с пустым $match {}

Ниже приведен запрос, который я запускаю в mongoDB, используя драйвер mongoose.

Model.aggregate([
  {"$match" : query},
  { $group : {_id: {country: "$Country", exporter: "$Exporter"}, id: {$first: "$_id"}, product: { $first: "$Description" }}},
  { $group : {_id: "$_id.country", data: {$push: { id: "$id", company: "$_id.exporter", product: "$product" }}, count:{$sum:1}}},
  { "$sort": { "count": -1 } },
  { 
    $project: { 
      "data": { "$slice": [ "$data", 3 ] },
      "_id": 1,
      "count": 1
    }
  },
]).allowDiskUse(true).explain()

где запрос создается динамически и по умолчанию пуст {} для поиска по всей коллекции. Индексированные поля

  1. Составной индекс: {Country: 1, Exporter: 1}

  2. Индекс текста: {Description: "text"}

Полный ответ объяснения():

{
"success": "Successfull",
"status": 200,
"data": {
    "stages": [
        {
            "$cursor": {
                "query": {},
                "fields": {
                    "Country": 1,
                    "Description": 1,
                    "Exporter": 1,
                    "_id": 1
                },
                "queryPlanner": {
                    "plannerVersion": 1,
                    "namespace": "db.OpenExportData",
                    "indexFilterSet": false,
                    "parsedQuery": {},
                    "winningPlan": {
                        "stage": "COLLSCAN",
                        "direction": "forward"
                    },
                    "rejectedPlans": []
                }
            }
        },
        {
            "$group": {
                "_id": {
                    "country": "$Country",
                    "exporter": "$Exporter"
                },
                "id": {
                    "$first": "$_id"
                },
                "product": {
                    "$first": "$Description"
                }
            }
        },
        {
            "$group": {
                "_id": "$_id.country",
                "data": {
                    "$push": {
                        "id": "$id",
                        "company": "$_id.exporter",
                        "product": "$product"
                    }
                },
                "count": {
                    "$sum": {
                        "$const": 1
                    }
                }
            }
        },
        {
            "$sort": {
                "sortKey": {
                    "count": -1
                }
            }
        },
        {
            "$project": {
                "_id": true,
                "count": true,
                "data": {
                    "$slice": [
                        "$data",
                        {
                            "$const": 3
                        }
                    ]
                }
            }
        }
    ],
    "ok": 1
}
}

Размер коллекции: 9 264 947 записей и 10,2 ГБ

Время отклика: 32154 мс

Запрос становится медленнее по мере увеличения размера моей коллекции.


person Abhay Verma    schedule 10.01.2019    source источник


Ответы (2)


Если ваш запрос равен {}, движок mongo пропускает этап $match и сразу переходит к этапу $group. Индекс использоваться не будет. Вы можете проверить выше из explain() результата. Операторы конвейера $match и $sort могут воспользоваться преимуществами индекса, если они встречаются в начале конвейера. Глядя на воронку продаж, вы группируете их по параметрам Страна и Экспортер. Что вы можете сделать, так это создать индекс для {Country: 1, Exporter: 1} и использовать $sort для {Country: 1, Exporter: 1} в качестве первого этапа конвейера. Это сделает $group более эффективным.

person simagix    schedule 13.01.2019
comment
Да, это имеет смысл. Но сортировка не помогает там, где у меня есть индексация страны и экспортера. Просто оказалось, что это неизбежно прямо сейчас, и мы не собираемся предоставлять эту функцию конечным пользователям, где они могут просматривать сводку общих записей, поскольку это будет огромная коллекция. Спасибо за всю информацию, ребята! Ваше здоровье! - person Abhay Verma; 22.01.2019

Использование такого агрегата означает, что mongodb должен просмотреть всю запись, затем сгруппировать данные (загрузить 10 ГБ), а затем разрезать созданный массив.

Конечно, чем больше растет ваша коллекция, тем она длиннее.


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


Я бы сначала find назвал каждое название страны, используя один запрос. Затем используйте один запрос для каждой страны, получающей первых 3 экспортеров.

Использование индексов по стране и экспортеру.

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

И учитывая, что там не тысячи разных стран

person Orelsanpls    schedule 10.01.2019
comment
Одним из основных требований здесь является поиск сводки уникальных экспортеров в каждой стране, и я чувствую, что создание группы по стране и экспортерам будет обязательным. Пожалуйста, дайте мне знать, если есть другой способ сделать это. Спасибо! - person Abhay Verma; 10.01.2019
comment
Это способ использовать определенный индекс для этих полей с group в совокупности? - person Abhay Verma; 10.01.2019