У меня есть около 50 M документа в моей базе данных Mongo с именем dma
, и я использую это объединение для получения необходимых данных res
, а затем манипулирую ими.
async function FormContract(ownerRealm, id) {
try {
const res = await collection.aggregate([
{
$match: {
date: {$gt:moment.utc().subtract(1, 'days').toDate(), $lt:moment.utc().toDate()},
id: id, //45 values one by one
ownerRealm: {$in: ownerRealm} //20 values one-by-one
}
},
{
$group: {
_id: "$lastModified",
open_interest: {$sum: "$buyout"},
min: {$min: "$price"},
min_size: {$min: {$cond: [{$gte: ["$quantity", 200]}, "$price", "$min:$price"]}},
avg: {$avg: "$price"},
max: {$max: "$price"},
max_size: {$max: {$cond: [{$gte: ["$quantity", 200]}, "$price", "$max:$price"]}},
stdDevPop: {$stdDevPop: "$price"},
cp: {$addToSet: "$owner"},
}
}]).exec();...
Схема моей коллекции выглядит так:
auc: { type: String },
id: { type: Number },
owner: { type: String },
ownerRealm: { type: String }, //20 unique values
bid: { type: Number },
buyout: { type: Number },
price: { type: Number },
quantity: { type: Number},
timeLeft: { type: String },
lastModified: { type: Number },
date: { type: Date, required: true }
На данный момент есть пара слов о моей проблеме.
У меня ~ 15 M документов, покрытых этим $match
запросом date: {$gt:moment.utc().subtract(1, 'days').toDate(), $lt:moment.utc().toDate()},
Просто чтобы быть уверенным, мне нужны были только новые документы на последний день, поэтому, очевидно, они будут вставлены в конец коллекции
А затем мне нужно сделать 20 (realms) x 45 (id) aggregation
запросов после него. Моя функция работает нормально, но в конечном итоге она выполняется .. эм долго.
Помните, я не говорю, что это медленно!
Вариант использования:
Если я использую for (ownerRealms.length)
внутри for(id.length)
for (let i = 0; i < ownerRealms.length; i++) {
for (let j = 0; j < id.length; j++) {
FormContract(ownerRealm, id)
что 20 servers x 45 id = 900
запросы будут формировать необходимые данные один
по одному без высокой нагрузки на процессор и оперативную память в течение ~ 20 минут для каждого
сервер или 20 mins x 20 servers = 400 mins
или ~ 6 ЧАСОВ!
или если я использую for(id.length)
и map(servers)
for (let j = 0; j < id.length; j++) {
const data = await Promise.all(ownerRealms.map(async ({ ... }) => {
FormContract(ownerRealm, id)
это было бы асинхронно
20 $aggregation
запросов по одному 45 раз. Или 1 час на 10 идентификаторов
или ~ 4,5 часа ( гораздо лучше, верно? ), но это создает большую нагрузку на
Процессор (почти все ядра при загрузке 90%, больной)
Я работаю на машине с 7 ядрами, 7 ГБ ОЗУ и 80'000 + IOPS SSD, так что, думаю, я делаю что-то не так, если я принимаю 90% загрузки процессора в течение 4,5 часов
Так есть ли идеи, что будет лучше для оптимизации? Прежде всего я создаю индекс в этой коллекции, и он выглядит так:
lastModified:-1
ownerRealm:1
date:-1
id:-1
Но, согласно Mongo Compass, он используется 0 раз и хранится в отдельной коллекции БД под названием undefinded
. Рис здесь:

Так в чем проблема? Я ошибаюсь? Должен ли я создать лучший индекс. Или есть другой способ, который я упускаю, например, хранение данных на этапе $match
где-то еще? Или использовать $sort
stage? Я прочитал, что запрос $match
должен быть первым, потому что он вызывает всю коллекцию.
Я уже проверял это StOv вопросы: Оптимизация индекса для структуры агрегации mongodb и Повышение производительности MongoDB с помощью сортировки и уже проверка dba.stackexchange
на что-то полезное .