время выполнения mongodb отличается от реального выполнения

#mongodb #mongodb-query

Вопрос:

Я использую M10 mongodb atlas (2 ГБ оперативной памяти, 2CPU) для тестирования. У меня в коллекции 101330 документов. Каждый документ выглядит так

 {  _id: ObjectId("618d3bf93de582a797f28df3"),  applicantName: 'a57cgMwpX6EsMca',  amount: 6391,  status: 'Pending',  date: ISODate("2021-11-11T15:51:21.115Z") }   

Используя оболочку mongo, я запускаю запрос db.test.find({}).toArray (), чтобы вернуть все записи. Это не завершается за 1 минуту. Я был удивлен, что это занимает так много времени. Это потому, что я использую мало ресурсов, или это нормально. Выполнение команды explain для просмотра подробных сведений с помощью db.test.find({}).explain(«executionStats») дало результат.

 {  queryPlanner: {  plannerVersion: 1,  namespace: 'policymatrix_dev.indextest',  indexFilterSet: false,  parsedQuery: {},  winningPlan: { stage: 'COLLSCAN', direction: 'forward' },  rejectedPlans: []  },  executionStats: {  executionSuccess: true,  nReturned: 101330,  executionTimeMillis: 26,  totalKeysExamined: 0,  totalDocsExamined: 101330,  executionStages: {  stage: 'COLLSCAN',  nReturned: 101330,  executionTimeMillisEstimate: 2,  works: 101332,  advanced: 101330,  needTime: 1,  needYield: 0,  saveState: 101,  restoreState: 101,  isEOF: 1,  direction: 'forward',  docsExamined: 101330  }  },  serverInfo: {  host: '',  port: 27017,  version: '4.4.10',  gitVersion: ''  },  ok: 1,  '$clusterTime': {  clusterTime: Timestamp({ t: 1636723868, i: 1 }),  signature: {  hash:   keyId: Long("6968661901790150660")  }  },  operationTime: Timestamp({ t: 1636723868, i: 1 }) }   

Из результата я вижу, что время выполнения равно 26 миллионам, что очень мало. Теперь я в замешательстве, почему существует разница в реальном времени выполнения запроса и времени выполнения статистики. Являются ли они одним и тем же или разными?

Чтобы добавить больше, я запускаю тот же запрос в функции AWS lambda. Он завершился за 5314 миллисекунд. Это становится действительно запутанным.

 module.exports.handler = async() =gt; {  try {  const db = await connectDB("database");   const start = new Date();  const response = await test.find({});  const end = new Date();   console.log("time required", end - start) // returns 5314  console.log(response.length) // returns 101330   return {success: true}  }  

Ответ №1:

При запуске db.test.find({}).explain("executionStats") вы получаете обратно один документ. Время выполнения будет включать время, затраченное на извлечение с диска/кэша, но эти документы фактически никогда не покидают сервер.

Когда вы бежите db.test.find({}).toArray() , за сценой происходит много такого, чего вы не видите.

Анатомия этого запроса выглядит примерно так:

  1. драйвер отправляет запрос «найти» на сервер
  2. mongod принимает глобальные блокировки, блокировки баз данных и блокировку коллекций
  3. mongod создает новый курсор для извлечения документов
  4. Когда документы возвращаются из механизма хранения, mongod создает объект ответа
  5. Когда объект ответа достигает предела размера объекта BSON в 16 МБ, mongod снимает блокировки и отправляет документ водителю
  6. драйвер получает объект ответа, содержащий несколько документов
  7. драйвер повторяет эти возвращенные документы, помещая каждый из них в массив
  8. когда объект ответа исчерпан, отправляет запрос getMore на сервер
  9. mongod повторно получает блокировки и возобновляет курсор
  10. повторяйте шаги 4-9, пока курсор не будет исчерпан
  11. верните конечный массив вызывающему процессу

Если у вас есть 100 тыс. документов со средним размером около 1 тыс., для этого потребуется не менее 6 поездок туда и обратно.

Время, указанное в пояснении, будет включать шаги 2,3 и 4. Если между объяснением и фактическим запуском произошли какие-либо изменения в использовании кэша, памяти, диска или процессора, эти действия не займут одинаковое количество времени.

Фактическое выполнение также приостанавливает выполнение на стороне сервера для шагов 5, 6, 7 и 8, поэтому, если обработка массива на стороне сети или клиента не является мгновенной (т. Е. в реальном мире), реальное выполнение займет больше времени.

Если вы хотите узнать подробности, вы можете просмотреть сетевые пакеты на клиенте и/или сервере, чтобы точно узнать, когда была запрошена и завершена каждая партия.

Вы также можете настроить параметры профилировщика, такие как slowms, чтобы получать запись в журнале для каждого пакета, в которой будут подробно описаны используемые блокировки и, возможно, время считывания с диска.

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

1. В этом есть смысл. Но как насчет разницы между запуском из mongosh и приложением? Я использовал драйвер mongodb nodejs в функции aws lambda. Это дает мне 5 секунд.

Ответ №2:

MongoDB использует алгоритм LRU(Сначала наименее последние) для удаления наименее последних данных из памяти, особенно когда у вас меньше ресурсов. Так что , похоже , происходит то, что данные находятся в хранилище и не используются в течение длительного времени, виртуальной машине потребуется некоторое время, чтобы загрузить их в память и обработать, особенно если вы не используете индексы, поэтому вы запускаете запрос, и он обрабатывает данные в течение gt; 1 минуты, но когда вы сразу же запускаете объяснение(«executionStats»), данные уже находятся в памяти, и для их обработки потребуется меньше времени. Если вы перезапустите службу или очистите кэш и выполните объяснение(«executionStats») до фактического запроса, он покажет вам время gt; 1 минуты( конечно, здесь это зависит от использования внутреннего хранилища).

Кроме того , уровни кластеров M10 и M20 поддерживают среды разработки и приложения с низким трафиком, они не предназначены для производственной и высокопроизводительной деятельности.