Mongodb $ expr запрос очень медленный - PullRequest
0 голосов
/ 17 октября 2019

У меня есть экземпляр 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(),
    },
  });

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

...