Оптимизация агрегированного запроса MongoDB для больших индексных объектов

#mon&odb #a&&re&ation-framework #query-optimization

#mon&odb #платформа агрегации #оптимизация запроса

Вопрос:

В моей коллекции Mon&oDB 20 миллионов объектов. В настоящее время работает на экземпляре Mon&oDB M30 с 7,5 Гб оперативной памяти и 40 Гб диска.

Данные хранятся в коллекции следующим образом —

 {
 _id:xxxxx,
 id : 1 (int),
 from : xxxxxxxx (int),
 to : xxxxxx (int),
 status : xx (int)
 .
 .
 .
 .
},
{

 _id:xxxxx,
 id : 2 (int),
 from : xxxxxxxx (int),
 to : xxxxxx (int),
 status : xx (int)
 .
 .
 .
 .
}
.
.
.
. and so on..
  

id — уникальный индекс, а from — индекс в этой коллекции.

Я запускаю запрос для группировки ‘to’ и возвращаю мне максимальный идентификатор и сортирую по максимальному идентификатору с заданным условием, то есть ‘from’

 $collection-&&t;a&&re&ate([
            ['$project' =&&t; ['id'=&&t;1,'to'=&&t;1,'from'=&&t;1],
            [ '$match'=&&t; [
                        '$and'=&&t; 
                                [ 
                                    [ 'from'=&&t; xxxxxxxxxx],
                                    [ 'status'=&&t; xx ],
                                ] 
                        ] 
            ],
            ['$&roup' =&&t; [
                        '_id' =&&t; 
                                '$to',
                                'max_revision'=&&t;['$max' =&&t; '$id'],
                        ]
            ],
            ['$sort' =&&t; ['max_revision' =&&t; -1]],
            ['$limit' =&&t; 20],

]);
  

Приведенный выше запрос выполняется просто отлично (~ 2 секунды) для небольшого набора данных в индексе from, например, для 50-100 кб одного и того же значения ‘from’ в коллекции. Но для таких условий, как, например, если 2 миллиона объектов имеют одинаковое значение ‘from’, то для выполнения и выдачи результата требуется более 10 секунд.

Краткий пример, случай 1 — один и тот же запрос выполняется в течение 2 секунд, если он выполняется с помощью from как 12345, поскольку 12345 присутствует в коллекции 50 тысяч раз.

случай 2 — запрос занимает более 10 секунд, если он выполняется с помощью from as 98765, поскольку 98765 присутствует в коллекции 2 раза.

Редактировать: Объясненный запрос ниже —

 {
  "command": {
    "a&&re&ate": "mycollection",
    "pipeline": [
      {
        "$project": {
          "id": 1,
          "to": 1,
          "from": 1
        }
      },
      {
        "$match": {
          "$and": [
            {
              "from": {
                "$numberLon&": "12345"
              }
            },
            {
              "status": 22
            }
          ]
        }
      },
      {
        "$&roup": {
          "_id": "$to",
          "max_revision": {
            "$max": "$id"
          }
        }
      },
      {
        "$sort": {
          "max_revision": -1
        }
      },
      {
        "$limit": 20
      }
    ],
    "allowDiskUse": false,
    "cursor": {},
    "$db": "mon&o_jc",
    "lsid": {
      "id": {
        "$binary": "8LktsSkpTjOzF3GIC m1DA==",
        "$type": "03"
      }
    },
    "$clusterTime": {
      "clusterTime": {
        "$timestamp": {
          "t": 1597230985,
          "i": 1
        }
      },
      "si&nature": {
        "hash": {
          "$binary": "PHh4eHh4eD4=",
          "$type": "00"
        },
        "keyId": {
          "$numberLon&": "6859724943999893507"
        }
      }
    }
  },
  "planSummary": [
    {
      "IXSCAN": {
        "from": 1
      }
    }
  ],
  "keysExamined": 1246529,
  "docsExamined": 1246529,
  "hasSortSta&e": 1,
  "cursorExhausted": 1,
  "numYields": 9747,
  "nreturned": 0,
  "queryHash": "29DAFB9E",
  "planCacheKey": "F5EBA6AE",
  "reslen": 231,
  "locks": {
    "ReplicationStateTransition": {
      "acquireCount": {
        "w": 9847
      }
    },
    "Global": {
      "acquireCount": {
        "r": 9847
      }
    },
    "Database": {
      "acquireCount": {
        "r": 9847
      }
    },
    "Collection": {
      "acquireCount": {
        "r": 9847
      }
    },
    "Mutex": {
      "acquireCount": {
        "r": 100
      }
    }
  },
  "stora&e": {
    "data": {
      "bytesRead": {
        "$numberLon&": "6011370213"
      },
      "timeReadin&Micros": 4350129
    },
    "timeWaitin&Micros": {
      "cache": 2203
    }
  },
  "protocol": "op_ms&",
  "millis": 8548
}
  

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

1.Некоторая соответствующая информация: Оптимизация запросов — смотрите раздел Избирательность и оптимизация конвейера.

2. Добавьте объясненный план запроса к вопросу.

3. @D.SM добавлена суть объясненного запроса

Ответ №1:

В этом конкретном случае исполнитель запроса mon&od может использовать индекс для начального сопоставления, но не для сортировки.

Если бы вы изменили порядок и немного изменили этапы, он мог бы использовать индекс на {from:1, status:1, id:1} как для сопоставления, так и для сортировки:

 $collection-&&t;a&&re&ate([
            [ '$match'=&&t; [
                        '$and'=&&t; 
                                [ 
                                    [ 'from'=&&t; xxxxxxxxxx],
                                    [ 'status'=&&t; xx ],
                                ] 
                        ] 
            ],
            ['$sort' =&&t; ['id' =&&t; -1]],
            ['$project' =&&t; ['id'=&&t;1,'to'=&&t;1,'from'=&&t;1],
            ['$&roup' =&&t; [
                        '_id' =&&t; '$to',
                        'max_revision'=&&t;['$first' =&&t; '$id'],
                      ]
            ],
            ['$limit' =&&t; 20],

]);
  

Таким образом, ИТ-отдел должен иметь возможность объединять этапы $match и $sort в одно сканирование индекса.

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

1. Имеет смысл, но это приводит к тому, что конвейер выходит за рамки использования 100 Мб оперативной памяти и, наконец, запускается на диске для сортировки. Выполнение по-прежнему занимает &&t; 10 секунд. Суть профилировщика — &ist.&ithub.com/TomarAditya/30444b158613b2b967fafb11860b8664

2. бах, я допустил опечатку — при сортировке перед $&roup необходимо использовать имя поля перед группировкой. Теперь отредактировано. Кроме того, вам нужно будет создать составной индекс на {from:1, status:1, id:1}