#mongodb #spring-data-jpa #spring-data
#mongodb #весна-данные-jpa #весна-данные
Вопрос:
Я использую spring boot, пытаясь извлечь набор документов из базы данных mongo (4.2). Необходимо получить около миллиона документов. Я использую пейджинг и получаю одновременно 100 документов.
findByCreatedDateLessThanAndStatusInAndFlagNot
Есть индекс на CreatedDate, есть 5 возможных статусов (я пытаюсь найти 2 из них). Когда я нахожу документ, я затем использую MongoTemplate, чтобы затем установить флаг как true (чтобы он не возвращался в следующем запросе). Я настраиваю подкачку так, чтобы она получала только страницу 0(обновление флага означало бы, что эти документы не будут возвращены при следующем вызове).
Первая тысяча звонков происходит быстро, примерно за секунду, но затем она просто начинает становиться все медленнее и медленнее, и некоторым звонкам требовалось 20 минут, чтобы вернуть результат. Любые мысли или идеи очень ценятся. Спасибо
Комментарии:
1. Вы также пробовали индексировать состояние и флаг?
2. У меня нет. Я думал, что, поскольку статус будет иметь только 5 возможных значений и флаг 2, индекс на самом деле не будет таким эффективным для них. Также не объясняет, как первые звонки были такими быстрыми
Ответ №1:
Этот запрос на последующих страницах будет хуже, чем проверка коллекции.
Из описания следует, что у вас есть документы, которые содержат:
{ CreatedDate: ISODate(), Status: "one", // of five Seen: false }
Существует указатель на
{ CreatedDate: 1}
И вы выполняете запрос, аналогичный:
Collection.find({ CreateDate:{$lte:ISODate()}, Status: {$in:["one","two"]}, Seen: false }).limit(100)
Затем вы извлекаете эти документы и отправляете (надеюсь, массовое) обновление, чтобы установить Seen
флаг true для каждого, а затем снова запускаете запрос, чтобы найти второй пакет.
Предполагая равномерное распределение значений статуса, поиск 2 из 5 подразумевает около 40% набора данных, поэтому 1 миллион возвращаемых документов предполагает наличие ~2,25 миллиона документов, соответствующих дате.
Как выполняется запрос
При первом запуске база данных начнет сканирование индекса, начиная с самого старого значения, загрузит документ в память и изучит поля Status
и. Seen
Если документ совпадает, он добавляется в результирующий набор, если нет, он отбрасывается и загружается следующий.
Когда результирующий набор достигает 100 документов, исполнитель запроса отправляет пакет и завершает запрос.
Предполагая, что статусы распределены равномерно, это будет означать, что 225 документов будут проверены, чтобы вернуть первые 100.
Пока все хорошо… но мы еще не закончили:
После обновления, когда будет запрошен второй пакет, база данных начнет сканирование индекса, начиная с самого старого значения, загрузит документ в память и изучит поля Status
и. Seen
Он не будет совпадать, потому Seen
что был обновлен до значения true после предыдущего запроса. Он повторно изучит первые 225 документов, соответствующих дате, и не найдет ничего, что можно было бы добавить к набору результатов. Вероятно, после этого ему придется изучить еще 225 документов, чтобы найти 100 совпадений.
По мере продвижения вперед 1000-й партии потребуется изучить 225 000 документов, которые были рассмотрены ранее, а затем 225, которые этого не сделали, 1001-й 225 225 225 и так далее.
Для возврата 1 миллиона документов 100 за один раз потребуется 10 000 партий. Заключительная партия проверит 2 250 000 документов, чтобы вернуть 100.
В какой-то момент, вполне вероятно, что некоторые из ранних документов придется удалить, чтобы освободить место для изучения более поздних. Когда это произойдет, с этого момента произойдет большой скачок в том, сколько времени займет каждая партия.
В целом за всю эту операцию первые 225 документов будут рассмотрены 10 000 раз, вторые 225 документов-9 999 раз и т.д. В общей сложности будет проведено более 11 миллиардов экспертиз документов, чтобы вернуть эти 1 миллион документов партиями по 100 штук.
Если вы используете компьютер, который может загружать и просматривать 1 000 000 документов в секунду, полная операция займет около 3 часов, чтобы определить миллион документов, которые необходимо вернуть, не считая сетевых поездок туда и обратно или обновлений.
Как это улучшить?
Создайте индекс на { CreatedDate: 1, Seen: 1 }
Если этот индекс доступен, база данных сможет пропустить уже просмотренные документы во время сканирования индекса, поэтому их не нужно будет проверять. Это означало бы, что вам нужно будет изучить только 225 документов для каждой партии, и общая операция вернет миллион документов всего за 2 250 000 проверок документов, что сократится на ~99,98%.
Чтобы улучшить еще немного:
Создайте индекс на { CreatedDate: 1, Status: 1, Seen: 1}
Если этот индекс доступен, при сканировании индекса база данных также может пропускать документы, которые не имеют нужного статуса. Это еще больше сократит количество проверок документов до 1 000 000, т. е. 1:1 при возврате документов.
Комментарии:
1. Вау, спасибо за это подробное объяснение, добавлю индекс и посмотрю, как все пойдет.
2. Я попытался добавить этот последний индекс. Каждый запрос занимал около 2 минут. Не знаю, куда идти дальше, чтобы улучшить его.
3. Проверьте,
explain
чтобы узнать, что он делает.