Лучшие практики управления и сокращения использования дискового пространства MongoDB
Оптимизируйте использование дискового пространства MongoDB с помощью этого всеобъемлющего руководства по лучшим практикам. Узнайте эффективные стратегии уплотнения коллекций и индексов, выявления и удаления ненужных индексов, а также использования функций сжатия WiredTiger. Откройте для себя методы архивирования данных, управления размером oplog и активного мониторинга дискового пространства для предотвращения сбоев системы и повышения производительности. Эта статья содержит практические советы и примеры для поддержания эффективности и компактности ваших развертываний MongoDB.
Лучшие практики управления и сокращения использования дискового пространства MongoDB
Использование дискового пространства MongoDB обычно становится критичным в самый неподходящий момент: пакетное задание выполняется дольше ожидаемого, удаление не освобождает место, или участник набора реплик предупреждает, что том почти заполнен. Исправление редко заключается в одной магической команде. Вам нужно знать, является ли пространство живыми данными, индексами, повторно используемым пространством WiredTiger, oplog, журналами или локальными резервными копиями.
Самый безопасный подход — сначала измерить, уменьшить то, что больше не должно существовать, и только затем выполнять более тяжелое обслуживание, такое как уплотнение или перестроение участников. Такой порядок позволяет избежать длительного события обслуживания, которое дает мало места.
Понимание потребления дискового пространства MongoDB
MongoDB использует дисковое пространство для нескольких компонентов:
- Файлы данных: Хранят фактические BSON-документы в коллекциях.
- Файлы индексов: Хранят B-деревья индексов, созданные для поддержки эффективного выполнения запросов.
- Файлы журнала (WiredTiger): Записывают операции записи до их применения к файлам данных, обеспечивая долговечность данных. Они предварительно выделяются.
- Oplog (операционный журнал): Специальная ограниченная коллекция в наборах реплик, которая записывает все операции записи. Необходим для репликации.
- Диагностические данные: Журналы, файлы процессов
mongodи другая системная информация.
Со временем из-за обновлений, удалений и роста документов (дополнения) коллекции и индексы могут фрагментироваться или содержать неиспользуемое выделенное пространство, что приводит к неэффективному использованию диска. Это "белое пространство" не возвращается операционной системе немедленно, даже если база данных больше не нуждается в нем для живых данных.
Стратегии сокращения дискового пространства MongoDB
1. Уплотнение коллекций и индексов
Операции уплотнения помогают вернуть неиспользуемое дисковое пространство путем более эффективной перезаписи файлов данных и индексов. Это может быть особенно полезно после значительных удалений или обновлений данных.
Уплотнение коллекций
В движке хранения WiredTiger (по умолчанию с MongoDB 3.2) compact в основном возвращает свободное пространство от удаленных документов и дефрагментирует коллекции. Он не перестраивает файл данных коллекции с нуля, как это делала операция compact в MMAPv1.
db.runCommand({ compact: "myCollection" })
Соображения по compact:
- Операции
compactмогут быть ресурсоемкими (ЦП, ввод-вывод) и занимать значительное время, особенно для больших коллекций. Их лучше всего выполнять в окна обслуживания или на вторичных участниках набора реплик. - Требования к диску и поведение блокировок различаются в зависимости от версии MongoDB, движка хранения и формы развертывания. Проверьте документацию для вашей точной версии перед запуском на большой производственной коллекции.
- Для шардированных кластеров запускайте
compactна каждом шарде независимо.
Перестроение индексов
Индексы также могут фрагментироваться. Перестроение индекса может вернуть пространство и потенциально улучшить производительность запросов.
db.myCollection.reIndex()
Соображения по reIndex():
- Поведение
reIndex()менялось в разных версиях MongoDB, и оно все еще может быть разрушительным на загруженных системах. Проверьте руководство для вашей версии, протестируйте на стенде и, где возможно, предпочтите поэтапную работу через участников набора реплик. - Как и
compact,reIndex()требует дополнительного дискового пространства во время операции.
repairDatabase (автономная операция)
При сильной фрагментации или повреждении данных repairDatabase может перестроить все файлы данных. Это автономная операция, требующая остановки экземпляра mongod.
mongod --repair
Предупреждение: repairDatabase следует использовать как крайнюю меру для возврата пространства, так как это разрушительная операция, если не обращаться с ней осторожно, и она может занять очень много времени. Всегда имейте резервную копию.
2. Оптимизация индексов
Индексы важны для производительности, но могут потреблять значительное дисковое пространство. Неиспользуемые или избыточные индексы — это чистые накладные расходы.
Выявление и удаление ненужных индексов
Регулярно проверяйте свои индексы, чтобы убедиться, что они все еще нужны.
- Список всех индексов для коллекции:
db.myCollection.getIndexes()
```
2. Мониторинг использования индексов: Используйте $indexStats, планы запросов, профилирование и историю рабочей нагрузки вашего приложения. Статистика коллекции показывает размер индекса, но не доказывает, полезен ли индекс.
3. Выявление дублирующихся или избыточных индексов: Например, индекс на { a: 1, b: 1 } делает индекс на { a: 1 } избыточным для запросов, которые могут использовать составной индекс. Индекс на { a: 1, b: 1 } также покрывается индексом на { a: 1, b: 1, c: 1 } для запросов, которые затрагивают только a и b.
После выявления удалите неиспользуемый индекс:
db.myCollection.dropIndex("indexName")
Совет: Всегда тестируйте влияние удаления индекса в среде стенда перед применением в производстве.
Использование частичных индексов
Частичные индексы индексируют только те документы в коллекции, которые удовлетворяют указанному выражению фильтра. Это уменьшает количество индексируемых документов, экономя дисковое пространство и улучшая производительность записи.
db.orders.createIndex(
{ customerId: 1, orderDate: -1 },
{ partialFilterExpression: { status: "active" } }
)
Этот индекс будет включать только документы, где status равен "active", уменьшая его размер, если большинство заказов являются историческими, отмененными, архивированными или иным образом находятся вне горячего пути. Важна не сама фраза "active"; важна привычка индексировать подмножество, которое ваше приложение фактически запрашивает каждый день.
Начните с триажа дискового пространства, а не с команды очистки
Когда дисковое пространство MongoDB растет, первая ошибка — сразу переходить к compact, repair или удалению старых данных. Эти действия могут помочь, но они также могут создать нагрузку, заблокировать некоторые ситуации или скрыть реальную проблему на несколько недель. Начните с ответа на три вопроса:
- Какая файловая система заполняется: путь к базе данных, путь к журналу, путь к журналам или том резервных копий?
- Растут ли живые данные или растет выделенное, но неиспользуемое пространство после удалений и обновлений?
- Исходит ли рост из коллекций, индексов, oplog, журналов, диагностических данных или снимков?
Быстрый первый проход обычно выглядит так:
df -h
du -h --max-depth=1 /var/lib/mongodb | sort -h
du -h --max-depth=1 /var/log/mongodb | sort -h
Затем проверьте MongoDB из оболочки:
db.adminCommand({ listDatabases: 1 })
db.getSiblingDB("app").stats()
db.getSiblingDB("app").orders.stats()
storageSize, totalIndexSize и dataSize рассказывают разные истории. Если dataSize растет, у вас, вероятно, проблема с жизненным циклом данных. Если storageSize намного больше, чем dataSize, вы, возможно, смотрите на повторно используемое внутреннее пространство после удалений. Если totalIndexSize велик по сравнению с dataSize, дизайн индекса заслуживает внимания, прежде чем вы коснетесь уплотнения.
Понимайте, что MongoDB может и не может вернуть
В WiredTiger удаление документов обычно делает пространство доступным для повторного использования MongoDB. Оно не всегда немедленно возвращает это пространство операционной системе. Такое поведение удивляет людей во время аварийной очистки: они удаляют большой пакет, запускают df -h и видят почти никакого улучшения.
Это не означает, что удаление не удалось. Это означает, что MongoDB часто может повторно использовать это пространство для будущих вставок и обновлений. Если цель — остановить рост, удаления или архивирования старых данных может быть достаточно. Если цель — уменьшить файловую систему, потому что том почти заполнен или хост уменьшается, вам может понадобиться уплотнение, повторная синхронизация участника набора реплик или перестроение типа дамп-и-восстановление.
Для производственных систем я обычно разделяю работу на два направления. Первое направление — немедленная безопасность: добавить диск, удалить очевидное накопление журналов, приостановить рискованные пакетные задания или переместить резервные копии с тома базы данных. Второе направление — реальное сокращение: исправить хранение, удалить неиспользуемые индексы и перестроить хранилище только после того, как вы узнаете, куда ушли байты.
Исправьте хранение данных перед любой дефрагментацией
Если ваше приложение хранит журналы запросов, события, сессии, уведомления, записи заданий или аналитические документы вечно, использование диска вернется, независимо от того, насколько тщательно вы уплотняете. MongoDB дает вам несколько практических вариантов.
Для данных, срок действия которых истекает по простой временной метке, TTL-индекс часто является самым чистым ответом:
db.sessions.createIndex(
{ expiresAt: 1 },
{ expireAfterSeconds: 0 }
)
Этот индекс удаляет документы после даты, хранящейся в expiresAt. Он полезен для сессий, временных токенов, краткосрочных заданий импорта или кэшированных ответов API. Он не заменяет бизнес-правила хранения. Монитор TTL работает в фоновом режиме, поэтому не ожидайте посекундного удаления и не используйте TTL для данных, требующих рабочего процесса утверждения перед удалением.
Для бизнес-записей архивируйте вместо слепого удаления. Распространенный шаблон:
- Скопировать документы старше окна хранения в более дешевое хранилище или архивную базу данных.
- Проверить количество и образец важных полей.
- Удалить небольшими пакетами из основной коллекции.
- Следить за задержкой репликации и метриками диска во время выполнения задания.
Небольшие пакеты имеют значение. Одно огромное удаление может создать давление репликации, заполнить журналы и усложнить откат, если кто-то поймет, что фильтр был неправильным. Более безопасное пакетное задание может удалять несколько тысяч документов за раз, ненадолго засыпать и записывать прогресс по _id или временной метке.
while (true) {
const result = db.events.deleteMany({
createdAt: { $lt: ISODate("2025-01-01T00:00:00Z") },
archived: true
});
print(`deleted ${result.deletedCount}`);
if (result.deletedCount === 0) break;
sleep(500);
}
В реальном производственном скрипте добавьте шаблон ограничения вместо deleteMany по всему диапазону, регистрируйте каждый пакет и автоматически останавливайтесь, если задержка репликации или дисковый ввод-вывод превышает ваш порог.
Будьте осторожны с советами по индексам, которые звучат слишком просто
Удаление неиспользуемых индексов — один из лучших способов уменьшить дисковое пространство MongoDB, но "неиспользуемый" требует контекста. Индекс может выглядеть неиспользуемым в тихую неделю и все еще быть критическим для отчетов за месяц, фоновой сверки или редкого рабочего процесса поддержки клиентов.
Используйте $indexStats, чтобы увидеть шаблоны доступа:
db.orders.aggregate([{ $indexStats: {} }])
Затем сравните результат с кодом приложения, запланированными заданиями, панелями мониторинга и запросами поддержки. Если индекс не использовался с момента последнего перезапуска, это сигнал, а не приговор. Перед удалением проверьте, не перезапускался ли сервер недавно и включает ли выборка рабочей нагрузки задания, которые имеют значение.
Также следите за перекрывающимися составными индексами. Если у вас есть такие:
{ customerId: 1 }
{ customerId: 1, createdAt: -1 }
{ customerId: 1, createdAt: -1, status: 1 }
вы можете удалить один, но только после проверки порядка сортировки, фильтров запросов и того, поддерживает ли более короткий индекс другой шаблон доступа. MongoDB может использовать левый префикс составного индекса, но это не означает, что самый большой индекс всегда является бесплатной заменой. Более крупные индексы стоят больше памяти и операций записи ввода-вывода, поэтому сохраните тот, который соответствует рабочей нагрузке, а не тот, который выглядит наиболее полным.
Предпочитайте повторную синхронизацию для больших операций сжатия в наборах реплик
Для большого набора реплик самый чистый способ вернуть дисковое пространство операционной системы — часто перестраивать одну вторичную реплику за раз. Основная идея:
- Подтвердите, что у вас есть здоровая репликация и актуальные резервные копии.
- Удалите или остановите вторичную реплику.
- Очистите ее локальный каталог данных.
- Позвольте ей повторно синхронизироваться с первичной или другой здоровой репликой.
- Повторите для следующей вторичной реплики.
- Понизьте первичную реплику в окно обслуживания и перестройте старую первичную последней.
Этот подход медленнее, чем выполнение команды, но о нем легче рассуждать, потому что каждый перестроенный участник записывает свежие файлы хранилища на основе текущих данных. Он также избегает попыток уплотнить каждую коллекцию под производственным трафиком. Это не бесплатно: начальная синхронизация может быть тяжелой для сети и диска, и вам нужно достаточно оставшихся участников, чтобы набор реплик оставался безопасным, пока один участник перестраивается.
Для автономного сервера MongoDB у вас нет такой роскоши. В этом случае запланируйте окно обслуживания, сделайте проверенную резервную копию и рассмотрите mongodump/mongorestore или миграцию на уровне файловой системы на новый том. Не выбирайте mongod --repair только потому, что хотите уменьшить каталог данных. Относитесь к восстановлению как к инструменту аварийного восстановления, а не к рутинному обслуживанию.
Следите за Oplog, журналами и резервными копиями
Не все давление на диск MongoDB исходит от коллекций. В наборах реплик oplog является ограниченной коллекцией, поэтому он не должен расти вечно, но его настроенный размер все равно имеет значение. Если он слишком мал, вторичные реплики могут отстать во время обслуживания. Если он намного больше, чем необходимо, на маленьком диске, он может тратить пространство. Проверьте его намеренно:
db.getSiblingDB("local").oplog.rs.stats()
Журналы MongoDB также могут заполнить диск, когда журналирование медленных запросов, уровень отладки или цикл ошибок приложения становятся шумными. Используйте ротацию журналов и храните журналы базы данных вдали от того же маленького тома, который хранит данные, когда это возможно.
Резервные копии — еще один частый сюрприз. Команды иногда запускают mongodump на том же хосте, потому что это удобно, а затем удивляются, почему срабатывают оповещения о диске во время окна резервного копирования. Резервная копия, хранящаяся на той же файловой системе, не является настоящей резервной копией и может привести MongoDB к худшему сбою во время уже рискованной операции. Потоковая передача резервных копий в объектное хранилище, на сервер резервного копирования или отдельный смонтированный том.
Практическое руководство по полному диску MongoDB
Если диск уже заполнен более чем на 90 процентов, замедлитесь и работайте в таком порядке:
- Подтвердите, принимает ли MongoDB записи и здоров ли набор реплик.
- Добавьте временную дисковую емкость, если позволяет платформа. Это часто безопаснее, чем аварийное удаление.
- Переместите или выполните ротацию слишком больших журналов и локальных файлов резервных копий.
- Остановите несущественные пакетные задания, которые интенсивно пишут.
- Определите самые большие коллекции и индексы с помощью
db.stats()иstats()коллекции. - Архивируйте или удаляйте только данные с четким правилом хранения.
- Планируйте уплотнение, повторную синхронизацию или восстановление после стабилизации системы.
Лучшее долгосрочное решение скучно: правила хранения, проверки индексов, оповещения о диске и проверенные процедуры перестроения. MongoDB комфортно повторно использует внутреннее свободное пространство, но операторам все равно нужно решать, какие данные заслуживают жизни на быстром хранилище, а какие можно переместить в другое место.