#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()
, за сценой происходит много такого, чего вы не видите.
Анатомия этого запроса выглядит примерно так:
- драйвер отправляет запрос «найти» на сервер
- mongod принимает глобальные блокировки, блокировки баз данных и блокировку коллекций
- mongod создает новый курсор для извлечения документов
- Когда документы возвращаются из механизма хранения, mongod создает объект ответа
- Когда объект ответа достигает предела размера объекта BSON в 16 МБ, mongod снимает блокировки и отправляет документ водителю
- драйвер получает объект ответа, содержащий несколько документов
- драйвер повторяет эти возвращенные документы, помещая каждый из них в массив
- когда объект ответа исчерпан, отправляет запрос getMore на сервер
- mongod повторно получает блокировки и возобновляет курсор
- повторяйте шаги 4-9, пока курсор не будет исчерпан
- верните конечный массив вызывающему процессу
Если у вас есть 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 поддерживают среды разработки и приложения с низким трафиком, они не предназначены для производственной и высокопроизводительной деятельности.