Перевод статьи подготовлен в преддверии старта курса «Нереляционные базы данных».
![](https://habrastorage.org/r/w1560/webt/m0/aj/8e/m0aj8e4cp6p0sgkdhcd3gdvj6qi.png)
Основные моменты:
Я очень давно работаю с базами данных, но только недавно открыл для себя MongoDB. Есть несколько вещей, которые я хотел бы знать перед началом работы с ней. Когда у человека уже есть опыт в определенной сфере, у него существуют предвзятые представления о том, что такое базы данных и что они делают. В надежде облегчить задачу понимания другим людям, представляю список распространенных ошибок.
К сожалению, MongoDB по умолчанию ставится без аутентификации. Для рабочей станции, доступ к которой устанавливается локально, такая практика нормальна. Но поскольку MongoDB – это многопользовательская система, которая любит использовать большие объемы памяти, будет лучше, если вы поставите ее на сервер с максимально возможным в ваших условиях количеством оперативной памяти, даже если собираетесь использовать ее только для разработки. Установка на сервер через порт по умолчанию может оказаться проблемной, особенно, если в запросе можно выполнить любой код на javascript (например,
Есть несколько методов аутентификации, но проще всего установить для пользователя ID/пароль. Воспользуйтесь этой идеей, пока будете думать над причудливой аутентификацией на основе LDAP. Если говорить о безопасности, то MongoDB должна постоянно обновляться, а логи всегда следует проверять на наличие несанкционированного доступа. Мне, например, нравится выбирать другой порт в качестве порта по умолчанию.
Чек-лист обеспечения безопасности MongoDB содержит хорошие советы для снижения риска проникновения в сеть и утечки данных. Легко отмахнуться и сказать, что сервер для разработки не нуждается в высоком уровне безопасности. Однако все не так просто и это относится ко всем серверам MongoDB. В частности, если нет веской причины использовать
MongoDB не использует схему. Но это не значит, что схема не нужна. Если вы хотите просто хранить документы без какой-либо согласованной схемы, сохранять их можно быстро и просто, но извлечь их потом может быть чертовски сложно.
Классическая статья «6 эмпирических правил для проектирования схем MongoDB» стоит того, чтобы ее прочитать, а такие функции, как Schema Explorer в стороннем инструменте Studio 3T, стоит использовать для регулярных проверок схем.
Забыв о порядке сортировки можно сильнее всего разочароваться и потерять больше времени, чем при использовании любой другой неправильной конфигурации. По умолчанию MongoBD использует бинарную сортировку. Но вряд ли она будет кому-то полезна. Чувствительные к регистру, ударению, бинарные сортировки считались любопытными анахронизмами наряду с бусами, кафтанами и завивающимися усами еще в 80-х годах прошлого века. Теперь же их использование непростительно. В реальной жизни «мотоцикл» – это то же самое, что и «Мотоцикл». А «Британия» и «британия» – одно и то же место. Строчная буква – это просто прописной эквивалент большой буквы. И не заставляйте меня говорить о сортировке диакритических знаков. При создании базы данных в MongoDB используйте параметры сортировки без учета ударения и регистра, которые соответствуют языку и культуре пользователей системы. Так вы значительно упростите поиск по строковым данным.
MongoDB рада разместить большие документы размером до 16 МБ в коллекциях, а GridFS предназначена для больших документов размером больше 16 МБ. Но только потому, что большие документы там можно разместить, хранить их там не лучшая идея. Лучше всего MongoDB будет работать, если вы будете сохранять отдельные документы размером в несколько килобайт, рассматривая их больше, как строки в широкой SQL-таблице. Большие документы будут источником проблем с производительностью.
Документы могут содержать массивы. Лучше всего, если количество элементов в массиве далеко от четырехзначного числа. Если элементы к массиву добавляются часто, он перерастет содержащий его документ, и его нужно будет переместить, значит, нужно будет обновить и индексы. При повторной индексации документа с большим массивом, индексы часто будут перезаписываться, поскольку под каждый элемент существует запись, хранящая его индекс. Такая переиндексация также происходит, когда документ вставляется или удаляется.
В MongoDB есть так называемый «коэффициент заполнения», который предоставляет пространство для роста документов, чтобы свести эту проблему к минимуму.
Вы можете подумать, что обойтись можно без индексации массивов. К сожалению, из-за отсутствия индексов у вас могут появиться другие проблемы. Поскольку документы просматриваются от начала до конца, поиск элементов в конце массива будет занимать больше времени, да и большинство операций, связанных с таким документом, будут медленными.
В системе базы данных с оптимизатором запросов, запросы, которые вы пишете, являются объяснениями того, что вы хотите получить, а не того, как это получить. Работает такой механизм по аналогии с заказом в ресторане: обычно вы просто заказываете блюдо, а не даете подробные инструкции повару.
В MongoDB вы инструктируете повара. Например, нужно убедиться, что данные проходят через
Такие инструменты как Studio 3T упростят построение запросов агрегации в MongoDB. Функция Aggregation Editor позволит вам применять операторы пайплайна по одному этапу за раз, а также проверять входные и выходные данные на каждом этапе для упрощения дебага.
Никогда не устанавливайте в MongoDB параметры записи с высокой скоростью, но низкой надежностью. Этот режим «file-and-forget» кажется быстрым, поскольку команда возвращается до того, как осуществляется запись. Если система упадет до того, как данные будут записаны на диск, они потеряются и окажутся в несогласованном состоянии. К счастью, в 64-битном MongoDB включено журналирование.
Движки для хранения MMAPv1 и WiredTiger используют логирование для предотвращения этого, хотя WiredTiger может восстановиться до последней согласованной контрольной точки, если журналирование отключено.
Журналирование гарантирует, что база данных находится в согласованном состоянии после восстановления и хранит все данные до момента записи в журнале. Периодичность записей настраивается с помощью параметра
Чтобы быть уверенным в записях, убедитесь, что в файле конфигурации журналирование включено
При поиске и агрегировании часто возникает необходимость в сортировке данных. Будем надеяться, что это делается на одном из заключительных этапов, после фильтрации результата с целью уменьшения объема сортируемых данных. И даже в таком случае для сортировки вам понадобится индекс. Можно воспользоваться одиночным или составным индексом.
Если подходящего индекса нет, MongoDB обойдется без него. Существует ограничение памяти в 32 Мб на общий размер всех документов в операции сортировки, и если MongoDB достигнет этого предела, то она либо выдаст ошибку, либо вернет пустой набор записей.
Поисковые запросы выполняют функцию аналогичную операции JOIN в SQL. Для лучшей работы им нужен индекс значения ключа, используемого в качестве внешнего ключа. Это неочевидно, поскольку использование не отражено в
Метод
В JSON объект состоит из неупорядоченной коллекции размером ноль или более пар имя/значение, где имя – это строка, а значение – это строка, число, логическое значение, ноль, объект или массив.
К сожалению, BSON придает большое значение порядку при поиске. В MongoDB порядок ключей внутри встроенных объектов имеет значение, т.е.
Значение «undefined» никогда не было допустимым в JSON, согласно официальному стандарту JSON (ECMA-404, Раздел 5), несмотря на то, что оно используется в JavaScript. Более того, для BSON оно устарело и преобразовывается в
Использование
Очень часто, когда вы ведете разработку в MongoDB, полезно просто увидеть образец результата, который вернется из запроса или агрегации. Для этой задачи вам пригодится
Единственный способ разочароваться в MongoDB – это сравнивать ее непосредственно с другим типом баз данных, таким как СУБД, или прийти к ее использованию, исходя из каких-то определенных ожиданий. Это все равно что сравнивать апельсин с вилкой. Системы баз данных преследуют определенные цели. Лучше всего просто понять и оценить для себя эти различия. Было бы стыдно давить на разработчиков MongoDB из-за пути, который вынудил их идти по пути СУБД. Мне хочется видеть новые и интересные способы решения старых проблем, таких как обеспечение целостности данных и создание систем данных, устойчивых к сбоям и атакам злоумышленников.
Внедрение в MongoDB в версии 4.0 ACID transactionality — хороший пример внедрения важных улучшений инновационным путем. Мультидокументальные и мультиоператорные транзакции теперь атомарные. Также появилась возможность регулировать время, необходимое для получения блокировок, и заканчивать зависшие транзакции, а также изменять уровень изоляции.
![](https://habrastorage.org/r/w1560/webt/2a/z8/xk/2az8xk2so48hq9b2ejyf3sjidzg.png)
![](https://habrastorage.org/webt/m0/aj/8e/m0aj8e4cp6p0sgkdhcd3gdvj6qi.png)
Основные моменты:
- Крайне важно разработать схему несмотря на то, что в MongoDB она необязательна.
- Аналогично, индексы должны соответствовать вашей схеме и шаблонами доступа.
- Избегайте использования больших объектов и больших массивов.
- Будьте осторожны с настройками MongoDB, особенно если речь идет о безопасности и надежности.
- В MongoDB нет оптимизатора запросов, поэтому вы должны быть осторожны при выполнении операций запроса.
Я очень давно работаю с базами данных, но только недавно открыл для себя MongoDB. Есть несколько вещей, которые я хотел бы знать перед началом работы с ней. Когда у человека уже есть опыт в определенной сфере, у него существуют предвзятые представления о том, что такое базы данных и что они делают. В надежде облегчить задачу понимания другим людям, представляю список распространенных ошибок.
Создание сервера MongoDB без аутентификации
К сожалению, MongoDB по умолчанию ставится без аутентификации. Для рабочей станции, доступ к которой устанавливается локально, такая практика нормальна. Но поскольку MongoDB – это многопользовательская система, которая любит использовать большие объемы памяти, будет лучше, если вы поставите ее на сервер с максимально возможным в ваших условиях количеством оперативной памяти, даже если собираетесь использовать ее только для разработки. Установка на сервер через порт по умолчанию может оказаться проблемной, особенно, если в запросе можно выполнить любой код на javascript (например,
$where
в качестве идеи для инъекции). Есть несколько методов аутентификации, но проще всего установить для пользователя ID/пароль. Воспользуйтесь этой идеей, пока будете думать над причудливой аутентификацией на основе LDAP. Если говорить о безопасности, то MongoDB должна постоянно обновляться, а логи всегда следует проверять на наличие несанкционированного доступа. Мне, например, нравится выбирать другой порт в качестве порта по умолчанию.
Не забудьте привязать поверхность атаки к MongoDB
Чек-лист обеспечения безопасности MongoDB содержит хорошие советы для снижения риска проникновения в сеть и утечки данных. Легко отмахнуться и сказать, что сервер для разработки не нуждается в высоком уровне безопасности. Однако все не так просто и это относится ко всем серверам MongoDB. В частности, если нет веской причины использовать
mapReduce
, group
или $where, нужно отключить использование произвольного кода на JavaScript, написав в файле конфигурации javascriptEnabled:false
. Поскольку в стандартной MongoDB файлы данных не зашифрованы, разумно запускать MongoDB с Dedicated User, у которого есть полный доступ к файлам, с ограниченным доступом только для него и возможностью использовать собственные средства управления доступом к файлам операционной системы.Ошибка при разработке схемы
MongoDB не использует схему. Но это не значит, что схема не нужна. Если вы хотите просто хранить документы без какой-либо согласованной схемы, сохранять их можно быстро и просто, но извлечь их потом может быть чертовски сложно.
Классическая статья «6 эмпирических правил для проектирования схем MongoDB» стоит того, чтобы ее прочитать, а такие функции, как Schema Explorer в стороннем инструменте Studio 3T, стоит использовать для регулярных проверок схем.
Не забудьте о порядке сортировки
Забыв о порядке сортировки можно сильнее всего разочароваться и потерять больше времени, чем при использовании любой другой неправильной конфигурации. По умолчанию MongoBD использует бинарную сортировку. Но вряд ли она будет кому-то полезна. Чувствительные к регистру, ударению, бинарные сортировки считались любопытными анахронизмами наряду с бусами, кафтанами и завивающимися усами еще в 80-х годах прошлого века. Теперь же их использование непростительно. В реальной жизни «мотоцикл» – это то же самое, что и «Мотоцикл». А «Британия» и «британия» – одно и то же место. Строчная буква – это просто прописной эквивалент большой буквы. И не заставляйте меня говорить о сортировке диакритических знаков. При создании базы данных в MongoDB используйте параметры сортировки без учета ударения и регистра, которые соответствуют языку и культуре пользователей системы. Так вы значительно упростите поиск по строковым данным.
Создание коллекций с большими документами
MongoDB рада разместить большие документы размером до 16 МБ в коллекциях, а GridFS предназначена для больших документов размером больше 16 МБ. Но только потому, что большие документы там можно разместить, хранить их там не лучшая идея. Лучше всего MongoDB будет работать, если вы будете сохранять отдельные документы размером в несколько килобайт, рассматривая их больше, как строки в широкой SQL-таблице. Большие документы будут источником проблем с производительностью.
Создание документов с большими массивами
Документы могут содержать массивы. Лучше всего, если количество элементов в массиве далеко от четырехзначного числа. Если элементы к массиву добавляются часто, он перерастет содержащий его документ, и его нужно будет переместить, значит, нужно будет обновить и индексы. При повторной индексации документа с большим массивом, индексы часто будут перезаписываться, поскольку под каждый элемент существует запись, хранящая его индекс. Такая переиндексация также происходит, когда документ вставляется или удаляется.
В MongoDB есть так называемый «коэффициент заполнения», который предоставляет пространство для роста документов, чтобы свести эту проблему к минимуму.
Вы можете подумать, что обойтись можно без индексации массивов. К сожалению, из-за отсутствия индексов у вас могут появиться другие проблемы. Поскольку документы просматриваются от начала до конца, поиск элементов в конце массива будет занимать больше времени, да и большинство операций, связанных с таким документом, будут медленными.
Не забудьте, что порядок стадий в агрегации имеет значение
В системе базы данных с оптимизатором запросов, запросы, которые вы пишете, являются объяснениями того, что вы хотите получить, а не того, как это получить. Работает такой механизм по аналогии с заказом в ресторане: обычно вы просто заказываете блюдо, а не даете подробные инструкции повару.
В MongoDB вы инструктируете повара. Например, нужно убедиться, что данные проходят через
reduce
как можно раньше в пайплайне с помощью $match
и $project
, а сортировка происходит только после reduce
, и что поиск происходит ровно в том порядке, в котором вам нужно. Наличие оптимизатора запросов, который избавляет от лишней работы, оптимально упорядочивает этапы и выбирает тип соединения, может вас избаловать. В MongoDB у вас появляется больше контроля ценой удобства.Такие инструменты как Studio 3T упростят построение запросов агрегации в MongoDB. Функция Aggregation Editor позволит вам применять операторы пайплайна по одному этапу за раз, а также проверять входные и выходные данные на каждом этапе для упрощения дебага.
Использование быстрой записи
Никогда не устанавливайте в MongoDB параметры записи с высокой скоростью, но низкой надежностью. Этот режим «file-and-forget» кажется быстрым, поскольку команда возвращается до того, как осуществляется запись. Если система упадет до того, как данные будут записаны на диск, они потеряются и окажутся в несогласованном состоянии. К счастью, в 64-битном MongoDB включено журналирование.
Движки для хранения MMAPv1 и WiredTiger используют логирование для предотвращения этого, хотя WiredTiger может восстановиться до последней согласованной контрольной точки, если журналирование отключено.
Журналирование гарантирует, что база данных находится в согласованном состоянии после восстановления и хранит все данные до момента записи в журнале. Периодичность записей настраивается с помощью параметра
commitIntervalMs
. Чтобы быть уверенным в записях, убедитесь, что в файле конфигурации журналирование включено
(storage.journal.enabled)
, а периодичность записей соответствует тому объему информации, который вы можете позволить себе потерять.Сортировка без индекса
При поиске и агрегировании часто возникает необходимость в сортировке данных. Будем надеяться, что это делается на одном из заключительных этапов, после фильтрации результата с целью уменьшения объема сортируемых данных. И даже в таком случае для сортировки вам понадобится индекс. Можно воспользоваться одиночным или составным индексом.
Если подходящего индекса нет, MongoDB обойдется без него. Существует ограничение памяти в 32 Мб на общий размер всех документов в операции сортировки, и если MongoDB достигнет этого предела, то она либо выдаст ошибку, либо вернет пустой набор записей.
Поиск без поддержки индексов
Поисковые запросы выполняют функцию аналогичную операции JOIN в SQL. Для лучшей работы им нужен индекс значения ключа, используемого в качестве внешнего ключа. Это неочевидно, поскольку использование не отражено в
explain()
. Такие индексы являются дополнением к индексу, записанному в explain()
, который в свою очередь используется операторами пайплайна $match
и $sort
, когда те встречаются в начале пайплайна. Индексы теперь могут охватывать любую стадию пайплайна агрегации. Отказ от использования мультиобновлений
Метод
db.collection.update()
используется для изменения части существующего документа или целого документа, вплоть до полной замены в зависимости от заданного вами параметра update
. Не так очевидно, что он не обработает все документы в коллекции, пока вы не установите параметр multi
для обновления всех документов, отвечающих критериям запроса.Не забудьте о важности порядка ключей в хэш-таблице
В JSON объект состоит из неупорядоченной коллекции размером ноль или более пар имя/значение, где имя – это строка, а значение – это строка, число, логическое значение, ноль, объект или массив.
К сожалению, BSON придает большое значение порядку при поиске. В MongoDB порядок ключей внутри встроенных объектов имеет значение, т.е.
{ firstname: "Phil", surname: "factor" }
– это не то же самое, что { { surname: "factor", firstname: "Phil" }
. То есть вы должны хранить в документах порядок пар имя/значение, если хотите быть уверены в том, что найдете их.Не путайте «null» и «undefined»
Значение «undefined» никогда не было допустимым в JSON, согласно официальному стандарту JSON (ECMA-404, Раздел 5), несмотря на то, что оно используется в JavaScript. Более того, для BSON оно устарело и преобразовывается в
$null
, что не всегда является хорошим решением. Избегайте использования «undefined» в MongoDB.Использование $limit()
без $sort()
Очень часто, когда вы ведете разработку в MongoDB, полезно просто увидеть образец результата, который вернется из запроса или агрегации. Для этой задачи вам пригодится
$limit()
, но его никогда не должно быть в финальной версии кода, если только перед ним вы не используете $sort
. Эта механика нужна, поскольку иначе вы не можете гарантировать порядок результата, и не сможете надежно просматривать данные. В верхней части результата вы будете получать разные записи в зависимости от сортировки. Для надежной работы запросы и агрегации должны быть детерминированными, то есть выдавать одинаковые результаты при каждом выполнении. Код, в котором есть $limit()
, но нет $sort
, не будет являться детерминированным и впоследствии может вызвать ошибки, которые будет трудно отследить.Заключение
Единственный способ разочароваться в MongoDB – это сравнивать ее непосредственно с другим типом баз данных, таким как СУБД, или прийти к ее использованию, исходя из каких-то определенных ожиданий. Это все равно что сравнивать апельсин с вилкой. Системы баз данных преследуют определенные цели. Лучше всего просто понять и оценить для себя эти различия. Было бы стыдно давить на разработчиков MongoDB из-за пути, который вынудил их идти по пути СУБД. Мне хочется видеть новые и интересные способы решения старых проблем, таких как обеспечение целостности данных и создание систем данных, устойчивых к сбоям и атакам злоумышленников.
Внедрение в MongoDB в версии 4.0 ACID transactionality — хороший пример внедрения важных улучшений инновационным путем. Мультидокументальные и мультиоператорные транзакции теперь атомарные. Также появилась возможность регулировать время, необходимое для получения блокировок, и заканчивать зависшие транзакции, а также изменять уровень изоляции.
![](https://habrastorage.org/webt/2a/z8/xk/2az8xk2so48hq9b2ejyf3sjidzg.png)