14 вещей, которые я хотел бы знать перед началом работы с MongoDB

Автор оригинала: Phil Factor
  • Перевод
Перевод статьи подготовлен в преддверии старта курса «Нереляционные базы данных».





Основные моменты:

  • Крайне важно разработать схему несмотря на то, что в 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 — хороший пример внедрения важных улучшений инновационным путем. Мультидокументальные и мультиоператорные транзакции теперь атомарные. Также появилась возможность регулировать время, необходимое для получения блокировок, и заканчивать зависшие транзакции, а также изменять уровень изоляции.


Читать ещё:


OTUS
Цифровые навыки от ведущих экспертов

Похожие публикации

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

    +30
    Я бы хотел знать другое:
    1. В Mongo нет нормальных бэкапов, вам предложат либо тормозной mongodump/mongorestore либо делать снапшоты дисков. Уже после этого пункта вам вряд ли захочется работать с mongo.
    2. В Mongo очень томозные курсоры. Если вы хотите пробежать по всей коллекции размеров в несколько сот гигабайт, то это будет очень долго. Так что в связке Mongo + Elastic переидексировать ваши гигабайты будет долгой задачей.
    3. У Mongo нет нормального бесплатного GUI, самое лучшее — это NoSQLBooster, но он стоит денег.
    4. У Mongo много ограничений, на размер документа, на блокировки и т.д. в cosmos лимиты ещё меньше.
    5. Mongo очень тяжело администрировать. В отличии от, например, Elasticsearch. Знакомые devops'ы при словах «конфигурировать mongo» как-то быстро сливались.
    6. Если вы думаете, что сейчас закините монгу в кубер и будет у вас сверхбыстрое хранилище документов на 10TB, то вы очень ошибаетесь.
    7. Mongo и Cosmos — это разные системы. Не стоит разрабатывать под Mongo в надежде, что оно потом легко заведётся на Cosmos.

    Обычно путь миграци на mongo проекта выглядит так:
    1. Решили перейти с Oracle/MS SQL/MySQL на mongo
    2. MVP выглядит отлично всё работает.
    3. Залили реальные данные, начали разворачивать кластер и погрустнели.
    4. Захотелось построить репорт.
    5. Внезапно кто-то из вспомнил про блокировки и ACID. Которые, в Mongo 4, конечно, есть, только вся ваша транзакция должна помещаться в один монго-документ размером в 16MB.
    6…
    7. Перешли на PostgreSQL.

    Моё ИМХО, что позиционирование mongo, как no-sql системы общего назначения крайне неправильно.
    Да, когда вы пишете MVP, то всё быстро.
    Да, там много фичей, но очень много подводных камней при работе с большими базами.
    И надо очень хорошо понимать, что у вас за данные, как проект будет развиваться и сможете ли вы найти достаточно компетентных devops'ов под Mongo.
    Иначе берите PostgreSQL и используйте json-поля. Если очень хочется no-sql, то присмотритесь к Elastic (в 2020ом он уже вполне себе no-sql база, а не временный индекс: снапшотить, админить и использовать его удобнее, чем Mongo).
    Либо сразу затачивайтесь на конкретное облако: cosmos или aws documentdb. Я не использовал MongoDB Atlas, но говорят, проблемы администрирования он лечит.
      +2

      5+ лет полета, терабайты данных, монга — всё хорошо. Так что надо понимать, что и как готовить. Эластик не панацея, там другие проблемы, другие данные и другие сценарии использования.

        0

        Я в 2012-2013 плотно работал с монгой в качестве быстрого кэша перед MS SQL, на тот момент в среднем раз в неделю один из 12 шардов умирал без явных причин. Т.к. это был кэш, не было проблемы его переинициализировать, но как единственную базу данных я бы монгу не стал использовать.
        У вас бывали подобные проблемы? Как решили?

          0
          Как вы бэкапили ваши терабайты Mongo-данных?
          Например, Elastic легко умеет заливать терабайтные снапшоты в S3 из коробки, причем делает это очень быстро.
          0
          Я несколько лет назад делал тест производительности mongo vs postgres в режиме key<>jsonb value. При тех настройках, которые у меня были postgres был быстрее.
          Мне показалась привлекательной возможность шардинга в mongo но знакомые девопсы меня отговорили.
          Так что похоже, лучшая no-sql документ ориентированная БД это Postgres
          +7
          К обучению ваша школа подходит также ответственно, как к переводу статей? То есть тяп-ляп гуглопереводчиком даже без исправления терминологии?

          Не забудьте привязать поверхность атаки к MongoDB

          Нужно сузить площадь атаки на MongoDB, а звать к себе на огонёк всем мамкиных хакеров.

          с Dedicated User, у которого есть полный доступ к файлам
          Run MongoDB with a Dedicated User with full access to the data files

          Полный доступ к файлам сервера? У него должен быть доступ только к базе данных.

          Установка на сервер через порт по умолчанию может оказаться проблемной
          To install it on a server on the default port without authentication is asking for trouble

          Куда вы слова про аутентификацию дели?

          Воспользуйтесь этой идеей, пока будете думать над причудливой аутентификацией на основе LDAP.
          Do that method while you think about your fancy LDAP-based authentication

          Суть «сначала поставьте пароль, а потом думайте, нужен ли вам модный LDAP» не передана.

          Если говорить о безопасности
          While we’re talking about security

          Да мы о ней уже второй абзац говорим, вы чего?

          MongoDB не использует схему. Но это не значит, что схема не нужна.

          А. ещё. можно. после. каждого. слова. точку. ставить. Да, в английском это звучит нормально, но тут у нас русский должен быть.

          Поиск без поддержки индексов

          Это не поиск, а операция соединения ($lookup) в одном из шагов конвейера аггрегации.

          команда возвращается до того, как осуществляется запись.

          Команда астронавтов с Марса у вас возвращается что ли? Команда сообщает об успешной записи.

          Чек-лист
          пайплайн
          логирование

          НовоязЪ вы знаете, молодцы, чО.
          Первое здесь — контрольный список, второе — конвейер, третье — журналирование.

          Забыв о порядке сортировки…

          Автор предлагает настраивать не порядок сортировки, а правила сортировки (текста/символов).

          которые соответствуют языку и культуре пользователей системы

          И тут не культура, а, скорее, диалект.

          с другим типом баз данных, таким как СУБД

          Монга, я сравню тебя с тобой же и разочаруюсь! Монга тоже СУБД, а с 4-й или даже 3-й версии её можно даже в РСУБД записать.
            +3
            А. ещё. можно. после. каждого. слова. точку. ставить. Да, в английском это звучит нормально, но тут у нас русский должен быть.

            Ну вот тут вы уже по мелочам придираетесь, нормально те два предложения читаются, если поставить там простую запятую — предложение потеряет структуру, прямо как то предложение, которое вы прямо сейчас читаете, хотя лично я бы там поставил не запятую, а точку с запятой, но, как я считаю, там можно и просто точку поставить, ведь русский язык этого не запрещает.


            НовоязЪ вы знаете, молодцы, чО.
            Первое здесь — контрольный список, второе — конвейер, третье — журналирование.

            Самое забавное тут, что логирование и журналирование — это совершенно разные термины, и в оригинале было именно journaling.

              0
              Шел 2020 год, люди почему-то все еще ждали действительно полезных и интересных статей на хабре от бизнес-аккаунтов
                +1
                Так интересные и полезные статьи таки бывают, например от Milfgard. Postgres Professional, ТуТу тоже ахинею не пишут, хоть и не для всех тема интересна, у КРОКа вроде что-то хорошее проскакивало, уверен есть много примеров хороших статей от бизнес учёток.
                Ну и как бы, если достаточно долго говорить людям, что они делают плохо, то до них дойдёт, что нужно исправляться или закрыться нафиг.
              0
              Сталкивался с MondoDB на двух проектах.

              На одном MondoDB использовался как хранилище логов — все ок.
              На другом MongoDB использовался как основная бд — и с ней было куча мороки. Например, просят доработать сортировку элементов, смотрю — а она в одних местах работает, в других нет. Иду в базу — там параметр, отвечающий за сортировку где-то есть, где-то нет, где-то он int, где-то string. Конечно, можно поправить, и поправил, но, в нормально спроектированной SQL-базе таких ошибок бы даже не появилось.
                0

                В SQL базах тоже проблем хватает. Вот мы настроили версионирование через миграции, даже вроде внимательно пишем Down миграции и вот налили что-то в прод и поняли, что не то и надо откатить… В 90% случаев такая история заканчивается восстановлением из бекапа и кучей ночной работы :(

                  0

                  Ну и в Монге будет то же самое — восстановление из бэкапа и куча ночной работы. Только там ещё и с бэкапами хуже.

                  –2
                  В нашем случае даже для журнала изменений объектов перешли с Монги на Постгрес:
                  • Скорость одиночной вставки — равная.
                  • Скорость пакетной вставки — постгрес с транзакциями существенно быстрее монги, даже если у неё отрубить журналирование. Нам для этой задачи было без разницы.
                  • Потребление памяти — постгрес 9.6 скромно кушал 15 — 100 метров оперативки, а монга v3.2 жрала 1,5 гига так как всю базу и индексы тащила в оперативку. Серверу наплевать, а вот в компах тогда было по 8 гиг, ситуация «на грани свопа».
                  • Скорость поиска — тут монга сливалась в унитаз. Без индексов по всем фильтруемым полям искать отказывалась, так как наша пара миллионов записей была > 150 000 (или 127к, точную цифру не вспомню), но даже обвешанная индексами монга сравнима только с постгресом без индексов. В основных сценариях «дай список изменений такого-то объекта» (int) и «дай мне все объекты у которых изменилось это поле» (json ключ -> значение) Постгрес с правильными индексами (jsonb_path_ops) укладывался в 0,05 секунды, монга же думала от 1 до 20 секунд.
                  • Место на диске — монге нужно было на треть меньше места. Не критичный параметр.

                  Для нас скорость поиска была критична, так что аривидерчи лёва Монга.

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

                Самое читаемое