Глава 5 — Когда использовать MongoDB

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

Для меня самым большим уроком, не имеющим, впрочем, ничего общего с MongoDB, стало то, что не обязательно полагаться на единственное решение для работы с данными. Естественно, единственное решение имеет очевидные преимущества, и для многих — если не для большинства — проектов таковое является разумным подходом. Смысл не в том, что вы должны, а скорее в том, что вы можете использовать различные технологии. Только вы знаете, перевешивают ли преимущества от внедрения нового решения возможные издержки.

С учётом сказанного, я надеюсь, что виденное вами ранее позволило вам расценивать MongoDB в качестве общего решения. Пару раз упоминалось, что документ-ориентированные базы данных имеют много общего с реляционными. Таким образом, чтобы не ходить вокруг да около, позвольте просто заявить, что MongoDB может рассматриваться как прямая альтернатива реляционным базам данных. В то время, как Lucene можно рассматривать, как расширение реляционных баз полнотекстовым индексом, а Redis — как персистентное хранилище ключ-значение, MongoDB — это центральный репозиторий для ваших данных.

Заметьте, я не называю MongoDB заменой реляционных баз, это скорее альтернатива. Это инструмент, который может делать то же, что могут делать множество прочих. Кое-что — лучше, кое-что — нет. Проанализируем это чуть позже.

Бесструктурность

Часто рекламируемым преимуществом документ-ориентированных баз данных является то, что они бесструктурны. Это делает их гораздо более гибкими, нежели традиционные реляционные базы данных. Я согласен, что бесструктурность хороша, но только не в качестве упоминаемого многими главного преимущества.

Часто бесструктурность видится как хаотичная организация данных. Есть домены и наборы данных, которые и правда очень трудно смоделировать в терминах обычной реляционной базы данных, но я рассматриваю их скорее как граничные случаи. Бесструктурность заманчива, однако большая часть данных должна быть хорошо структурированной. Конечно, иногда это может быть удобно, особенно для добавления нового функционала, однако на деле это можно решить и добавлениями новых необязательных полей.

Для меня настоящее преимущество бесструктурной архитектуры — это отсутствие установки и сведённые к минимуму расхождения с ООП. Особенно это чувствуется при работе со статически типизированными языками. Я работал с MongoDB как в C#, так и в Ruby — разница бросается в глаза. Динамизм Ruby и популярная реализация ActiveRecord уже ощутимо сокращают расхождение объектной и реляционной моделей (object-relational impedance mismatch). Это не означает, что MongoDB — плохое решение для Ruby, напротив. Скорее я думаю, что большинство Ruby-разработчиков видят MongoDB как небольшое улучшение, в то время как разработчики, пишущие на C# или Java, видят пропасть разделяющую MongoDB и их подход к манипулированию данными.

Подумайте об этом с точки зрения разработчика драйверов. Вам надо сохранить объект? Сериализируйте его в JSON (на самом деле в BSON, но это почти одно и то же) и отправьте в MongoDB. Нет никакого маппинга свойств или типов. Эта простота определённо должна подходить вам, как конечному разработчику.

Запись

Область, для которой MongoDB особенно подходит, — это логгирование. Есть два аспекта MongoDB, которые делают запись быстрой. Во-первых, можно отправить команду записи и продолжить работу, не ожидая её возврата и действительной свершившейся записи. Во-вторых, с появлением в версии 1.8 журналирования и некоторыми улучшениями, сделанными в версии 2.0, стало возможно контролировать поведение записи с учётом целостности данных. Эти параметры, в дополнение к тому, сколько серверов должны получить ваши данные, прежде чем запись будет считаться успешной, настраиваются на уровне отдельной записи, что даёт вам большую степень контроля над выполнением записи данных и их долговечностью.

Кроме указанных факторов производительности, при логгировании как раз может оказаться полезной гибкая структура данных. Наконец, в MongoDB есть такое понятие, как ограниченная коллекция (capped collection). До сих пор мы создавали обыкновенные коллекции. Мы можем создать ограниченную коллекцию с помощью команды db.createCollection, включив флаг capped:

//ограничиваем размер коллекции до 1 мегабайта
db.createCollection('logs', {capped: true, size: 1048576})

Когда наша ограниченная коллекция достигнет размера в 1 мегабайт, старые документы начнут автоматически удаляться. Можно также задать не размер коллекции, а максимальное количество документов, с помощью опции max. У ограниченных коллекций есть ряд интересных свойств. Например, можно изменить документ, но он не может вырасти в размере. Также сохраняется порядок вставки, так что не нужно добавлять дополнительное поле для хронологической сортировки.

Также стоит заметить, что если нужно выяснить, вызвала ли ваша запись какие-либо ошибки (как, например, в уже упомянутом случае, когда мы не дожидаемся её завершения), можно просто выполнить следующую команду: db.getLastError(). Большинство драйверов инкапсулируют эту функцию, как безопасную запись, например, можно указать {:safe => true} вторым параметром метода insert.

Устойчивость

MongoDB до версии 1.8 не обеспечивала устойчивости данных на одном сервере. Так, отказ сервера мог привести к потере данных. Решение всегда состояло в работе MongoDB на нескольких серверах (MongoDB поддерживает репликацию). Одной из самых важных функций, добавленных в MongoDB 1.8, стало журналирование. Чтобы включить его, добавьте journal=true в файл mongodb.config, созданный нами при первой настройке MongoDB (и перезапустите сервер, чтобы изменения вступили в силу). Скорее всего, журналирование вам понадобится (в следующих релизах по умолчанию оно будет включено). Несмотря на некоторое увеличение производительности, которое может быть достигнуто при отключении журналирования, возможен определенный риск. (С другой стороны, бывают приложения, которые допускают потерю некоторых данных).

Устойчивость данных упоминается здесь потому, что много сил было затрачено для того, чтобы добиться её в пределах одного сервера. Вы рано или поздно найдёте в Google упоминания о ненадёжности Mongo как хранилища. Однако эта информация уже устарела.

Полнотекстовый поиск

В будущих релизах, надеюсь, полнотекстовый поиск придёт в MongoDB. С поддержкой для массивов базовый полнотекстовый поиск будет довольно просто применять. Для мощных приложений скорее всего понадобится использовать нечто вроде Lucene или Solr. Конечно также это справедливо и для реляционных баз данных.

Транзакции

MongoDB не поддерживает транзакций. Есть две альтернативы: одна — замечательная, но ограниченная в использовании, а другая — громоздкая, но гибкая.

Первая альтернатива — это множество атомарных операций. Они прекрасны до тех пор, пока решают вашу проблему. Мы уже видели некоторые из них, например, $inc и $set. Также существуют команды вроде findAndModify которые могут обновлять или удалять документ и автоматически его возвращать.

Вторая альтернатива — когда атомарных операций не хватает — это двухфазный коммит. Двухфазный коммит по сравнению с транзакциями — это примерно то же самое, что ручное разруливание запросов по сравнению с JOIN-ами. Это независимое от хранилища решение, которое вы осуществляете в коде. Также двухфазный коммит достаточно распространён в реляционном мире, когда нужно обеспечить транзакции в пределах нескольких баз данных. На сайте MongoDB есть пример иллюстрирующий наиболее распространённый сценарий (перевод денежных средств). Общая идея состоит в том, что вы храните состояние транзакции внутри обновляющегося документа и проходите шаги init-pending-commit/rollback вручную.

Поддержка вложенных документов и бесструктурная архитектура MongoDB делают двухфазные коммиты не такими уж страшными, но всё равно это сложный процесс, особенно для тех, кто впервые с этим сталкивается.

Обработка данных

Для большинства задач обработки данных MongoDB использует MapReduce. Есть, конечно, некоторые базовые агрегирующие функции, но для чего-либо серьёзного вам понадобится MapReduce. В следующей главе мы рассмотрим MapReduce более детально. Сейчас можете считать его очень мощным и альтернативным вариантом group by (что, впрочем, будет преуменьшением его возможностей). Одно из преимуществ MapReduce в том, что для работы с большими объёмами данных он может выполняться параллельно. Однако реализация MongoDB основана на JavaScript, который сам по себе однопоточен. Что из этого следует? Для обработки больших данных вам, скорее всего, придётся полагаться на что-то другое, например, на Hadoop. К счастью, эти две системы настолько дополняют друг друга, что существует MongoDB адаптер для Hadoop.

Конечно, распараллеливание обработки данных не является однозначным предметом превосходства реляционных баз данных. В будущих релизах MongoDB планируется улучшить обработку огромных объёмов данных.

Геопространственные данные

Особенно мощной функцией MongoDB является её поддержка геопространственных индексов. Это позволяет сохранять x- и y-координаты у документов и затем находить документы вблизи ($near) определённых координат, или внутри ($within) прямоугольника либо окружности. Это легче понять визуально, поэтому я советую посмотреть пятиминутный практикум по геопространственным функциям MongoDB, если хотите углубить свои знания.

Инструментарий и зрелость

Вы уже, наверное, знаете — MongoDB значительно младше большинства реляционных баз данных. Это обязательно нужно учитывать. Насколько большую роль это играет — зависит от ваших задач и их реализации. Нельзя игнорировать тот факт, что MongoDB — молодая технология, и доступный инструментарий ещё не очень разнообразен (впрочем, инструментарий зрелых реляционных баз данных бывает подчас просто ужасен). Например, отсутствие поддержки десятичных чисел с плавающей запятой, очевидно, будет проблемой (хотя и не обязательно непреодолимой) для систем, имеющих дело с деньгами.

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

В этой главе

Идея этой главы в том, что MongoDB в большинстве случаев способна стать заменой реляционной базе данных. Она намного проще и понятнее; быстрее работает и имеет меньше ограничений для разработчиков приложений. Отсутствие транзакций может вызывать серьёзную и правомочную озабоченность. Однако, когда спрашивают какое место занимает MongoDB в экосистеме современных механизмов хранения?, ответ прост: строго посередине.


Мои друзья: