#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}