У меня есть экземпляр Mongo 4.2.0 в моей среде разработки с простой коллекцией всего из 300 записей.
Я создал базовую обработку жонглирования очередью с некоторыми полями даты.
Чтобы получить документ, который должен быть обновлен, у меня есть следующий $ expr-запрос, который выполняется очень медленно imho.
db.collection("myupdates").findOneAndUpdate({
$expr: {
$and: [
{ $gt: ["$shouldUpdate", "$updatedAt"] },
{ $gt: ["$shouldUpdate", "$isUpdatingAt"] },
{ $gt: ["$shouldUpdate", "$updateErroredAt"] },
]
},
}, {
$set: {
isUpdatingAt: new Date(),
},
});
Этот запрос занимает около ~ 120 мс после прогрева на моем стандартном ноутбуке 2019 года. В то время как мои другие простые запросы занимают всего ~ 3 мс.
Хотя на самом деле не имеет значения устанавливать индексы с 300 документами, я, конечно, пытался установить их все. Одиночные к составным индексам. Это не сработает.
Это также не findOneAndUpdate
, с countDocuments
я получаю такую же медленную скорость.
Это нормальная скорость синтаксиса $ expr или агрегации? Что я не прав? Есть ли лучший способ добиться этого? Нужно ли использовать Redis для этого варианта использования?
Возможное решение
Как указывалось в ответах @Neil Lunn
, вычисленные условия не используют индекс и должны быть последним средством.
Итак, я просто избавился от вычисленного условия, разделив запрос на 2 запроса. Первый запрос получает фактическое значение, с которым я могу соответствовать. Эти 2 запроса сводятся к общему объему ~ 10 мс, что намного лучше, чем 120 мс.
const shouldUpdateDateResult = await mongo.db.collection("myupdates").findOne({
shouldUpdate: { $exists: true }
}, {
shouldUpdate: 1,
});
const shouldUpdateDate = shouldUpdateDateResult && shouldUpdateDateResult.shouldUpdate;
const result = await mongo.db.collection("myupdates").findOneAndUpdate({
$and: [
{ shouldUpdate: shouldUpdateDate },
{ $or: [
{ updatedAt: { $eq: null } },
{ updatedAt: { $exists: false } },
{ updatedAt: { $lte: shouldUpdateDate } }
] },
{ $or: [
{ isUpdatingAt: { $eq: null } },
{ isUpdatingAt: { $exists: false } },
{ isUpdatingAt: { $lte: shouldUpdateDate } }
] },
{ $or: [
{ updateErroredAt: { $eq: null } },
{ updateErroredAt: { $exists: false } },
{ updateErroredAt: { $lte: shouldUpdateDate } }
] },
],
}, {
$set: {
isUpdatingAt: new Date(),
},
});
Вся идея в этом заключается в очереди обработки, которую могут использовать несколько рабочих.