Ответ на запрос MongoDB слишком медленный

#mongodb #mgo

#mongodb #mgo

Вопрос:

Я работаю над проектом Golang (db MongoDB). Я выполнил приведенный ниже запрос, но загрузка данных занимает слишком много времени. При этом я получаю данные из 2 коллекций с несколькими этапами.

 db.getCollection('Collection1').aggregate([
{
    "$lookup": {
        "localField": "uid",
        "from": "collection2",
        "foreignField": "_id",
        "as": "user_info"
    }
},
{
    "$unwind": "$user_info"
},
{
    "$lookup": {
        "localField": "cid",
        "from": "collection3",
        "foreignField": "_id",
        "as": "cust_info"
    }
},
{
    "$lookup": {
        "from": "logs",
        "let":  {"id": "$_id"},
        "pipeline": [
                {"$match": {"$expr": {"$eq": ["$$id", "$log_id"]}}},
                {"$sort": {"log_type": 1}}],
        "as": "logs_data"
    }
},
{
    "$sort": {"logs_data.logged_on":-1}
},
{
    "$skip": 1
},
{
    "$limit": 2
},
])
  

Мое требование — добавить сортировку по времени 2 в одном запросе:

  1. В массиве журналов "$sort": {"log_type": 1}}
  2. Для конечного результата "$sort": {"logs_data.logged_on":-1}

Для этого я попробовал следующие индексы:

 {"logged_on" : -1}
{"log_id":1, "log_type":1}
  

Но выполнение запроса занимает еще 6-7 секунд.

Если я удаляю "$sort": {"logs_data.logged_on":-1} , то он работает быстро, но при такой сортировке это занимает слишком много времени.

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

Комментарии:

1. Будет ли logs коллекция содержать какие-либо документы, которые не соответствуют _id from collection1 ?

2. @Joe Нет, это невозможно. Журналы не содержат документов, которые не соответствуют «_id» из коллекции1

Ответ №1:

Что делает эта агрегация:

  1. извлеките все документы из коллекции1
  2. для каждого документа в коллекции1 найдите один документ в коллекции2
  3. для каждого документа в коллекции1 найдите один документ в коллекции3
  4. для каждого документа в коллекции1 найдите все реализованные документы в журналах
  5. для каждого документа в коллекции1 выполните сортировку документов, извлеченных из журналов, в памяти
  6. выполните сортировку в памяти, чтобы упорядочить документы
  7. сохраните 2 из этих документов и выбросьте остальные

Для каждого документа в коллекции1 это 3 выборки документов (плюс неизвестное количество выборок в # 4), 2 сканирования индекса и сортировка в памяти.

Если в коллекции 1 имеется нетривиальное количество документов, это огромная работа, которая тратится впустую для всех документов, кроме 2.

Если можно с уверенностью предположить, что каждый документ в logs содержит log_id , который сопоставляется с collection1 , вы могли бы:

  • создайте индекс на {logged_on:1, log_id:1}
  • запустите агрегацию для коллекции журналов
  • Сортировать по logged_on: 1
  • проект {logged_on:1, log_id:1, _id:0} (это делает первую часть агрегации полностью покрытой указанным выше индексом)
  • группируйте по log_id , принимая $first значение logged_on
  • сортировать по logged_on: 1 (группировка распределяет сортировку)
  • пропустить и ограничить по желанию
  • поиск из collection1 с помощью локального log_id внешнего _id
  • Замените Root на newRoot, являющийся просмотренным документом
  • выполните существующие этапы конвейера, которые вы использовали — на этот раз они будут извлекать / сортировать только для 2 документов, которые вы хотите вернуть.