MongoDB сочетает в себе возможность масштабирования с такими функциями, как вторичные индексы, запросы диапазона, сортировка, агрегирование и геопространственные индексы. Это документально-ориентированная база данных, не являющаяся реляционной. Основной причиной отказа от реляционной модели является упрощение масштабирования, но есть и другие преимущества.
Документно-ориентированная база данных заменяет концепцию «строки» более гибкой моделью, «документом». С помощью внедрения документов и массивов, документно-ориентированный подход позволяет представлять сложные иерархические отношения в одной записи. Это естественным образом вписывается в концепцию, которую разработчики современных объектно-ориентированных языков имеют о своих данных.
В MongoDB также нет предопределенных схем: ключи и значения документа не имеют фиксированных типов или размеров. Без фиксированной схемы добавление или удаление полей по мере необходимости становится проще. Как правило, это ускоряет разработку, поскольку разработчики могут быстрее выполнять итерации. Также легче экспериментировать. Разработчики могут попробовать десятки моделей для данных, а затем выбрать лучшую из них.
Возможности базы данных
MongoDB предназначена для использования в качестве базы данных общего назначения, поэтому помимо создания, чтения, обновления и удаления данных она предоставляет постоянно растущий список уникальных функций:
- Индексирование. MongoDB поддерживает общие вторичные индексы, позволяя выполнять различные быстрые запросы, а также предоставляет уникальные, составные, геопространственные и полнотекстовые возможности индексации;
- Агрегирование. MongoDB поддерживает «конвейер агрегации», который позволяет создавать сложные агрегации из простых частей и позволяет базе данных оптимизировать их;
- Специальные типы коллекций. MongoDB поддерживает коллекции времени жизни для данных, срок действия которых должен истечь в определенное время, таких как сеансы. Он также поддерживает коллекции фиксированного размера, которые полезны для хранения последних данных, таких как журналы;
- Файловое хранилище. MongoDB поддерживает простой в использовании протокол для хранения больших файлов и метаданных файлов.
Некоторые функции, общие для реляционных баз данных, отсутствуют в MongoDB, в частности, объединения и сложные многострочные транзакции. Отказ от этих решений был архитектурным решением, обеспечивающим большую масштабируемость, поскольку обе эти функции сложно эффективно реализовать в распределенной системе.
Документы
В основе MongoDB лежит документ: упорядоченный набор ключей со связанными значениями. Представление документа зависит от языка программирования, но большинство языков имеют естественную структуру данных, например карту, хэш или словарь. В JavaScript, например, документы представлены в виде объектов:
Этот простой документ содержит один ключ "приветствие" со значением "Hello, world!". Большинство документов будут более сложными, чем этот, и часто будет содержать несколько пар ключ/значение:
Как видно из приведенного выше примера, значения в документах – это не просто "BLOB-объекты". Они могут относиться к одному из нескольких типов данных (или даже ко всему встроенному документу). В этом примере значение для "приветствия" является строкой, тогда как значение для "foo" является целым числом.
Ключи в документе являются строками. Любой символ UTF-8 допускается в ключе, за несколькими исключениями:
- В ключах должен отсутствовать нулевой символ . Он предназначен для обозначения конца ключа;
- Символы . и $ имеют некоторые специальные свойства и должны использоваться только при определенных обстоятельствах. В общем, они должны считаться зарезервированными, так что инициаторы будут жаловаться, если они используются не по назначению.
MongoDB чувствителен к типу и регистру. Например, эти документы отличаются друг от друга:
Да и эти тоже:
И еще одна важная деталь: документы в MongoDB не могут содержать дубликаты ключей. Например, следующее не является документом:
Пары ключ/значение в документах упорядочены: {"x": 1, "y": 2} не совпадает с {"y": 2, "x": 1}. Порядок полей обычно не имеет значения, и вы не должны разрабатывать свою схему так, чтобы она зависела от определенного порядка полей (MongoDB может изменить их порядок).
В некоторых языках программирования представление документа по умолчанию даже не поддерживает порядок (например, словари в Python и хэши в Perl или Ruby 1.8). Инициаторы для этих языков обычно имеют некоторый механизм для указания документов при заказе, когда это необходимо.
Вставка и сохранение документов
Вставка – это основной метод добавления данных в MongoDB. Чтобы вставить документ в коллекцию, используйте метод вставки коллекции:
Это добавит ключ "_id" к документу (если он еще не существует) и сохранит его в MongoDB.
Bulk insert
Если вам нужно вставить несколько документов в коллекцию, вы можете ускорить этот процесс используя пакетную вставку. Пакетные вставки позволяют передавать массив документов в базу данных.
Вы можете попробовать сделать это с помощью функции batchInsert, которая похожа на вставку, за исключением того, что для вставки требуется массив документов:
Отправка одновременно десятков, сотен или даже тысяч документов может значительно ускорить процесс вставки.
Пакетные вставки полезны, только если вы вставляете несколько документов в одну коллекцию: вы не можете использовать пакетные вставки для вставки в несколько коллекций с помощью одного запроса. Если вы просто импортируете необработанные данные (например, из фида данных или MySQL), есть инструменты командной строки, такие как mongoimport, которые можно использовать вместо пакетной вставки. С другой стороны, часто удобно разбирать данные перед их сохранением в MongoDB (преобразование даты в тип даты или добавление пользовательского"_id"), поэтому пакетные вставки также можно использовать для импорта данных.
Текущие версии MongoDB не принимают сообщения длиннее 48 МБ, поэтому существует ограничение на количество, которое можно вставить в одну пакетную вставку. Если вы попытаетесь вставить более 48 МБ, многие инициаторы разделят пакетную вставку на несколько 48 МБ. Проверьте документацию вашего инициатора для деталей.
Если вы импортируете пакет, а один документ из него "застревает" на пол пути и не может быть вставлен, документы до этого документа будут вставлены, а все, которые идут после него, – нет:
Тут будут вставлены только первые два документа, так как третий выдаст ошибку: вы не можете вставить два документа с одинаковым "_id".
Если вы хотите игнорировать ошибки и сделать попытку batchInsert вставить оставшуюся часть пакета, вы можете использовать опцию continueOnError, чтобы продолжить после сбоя вставки. Это вставит первый, второй и четвертый документы из примера выше. Оболочка не поддерживает эту опцию, но все инициаторы поддерживают.
Проверка вставки
MongoDB выполняет минимальные проверки вставляемых данных: он проверяет базовую структуру документа и добавляет поле "_id", если оно отсутствует. Одной из основных проверок структуры является размер: все документы должны быть меньше 16 МБ. Это не слишком жесткий предел (и может быть повышен в будущем), но он в основном служит для предотвращения неправильного дизайна схемы и обеспечивает стабильную производительность. Чтобы определить размер BSON (в байтах) документа документа, инициируйте Object.bsonsize (документ) из оболочки.
Чтобы дать вам представление о том, сколько данных составляет 16 МБ, весь текст «Войны и мира» составляет всего 3,14 МБ.
Эти минимальные проверки также означают, что довольно просто вставить неверные данные (если вы пытаетесь это сделать). Таким образом, вы должны разрешать только надежные источники, такие как серверы приложений, для подключения к базе данных. Все инициаторы для основных языков (и большинства второстепенных тоже) проверяют множество недопустимых данных (слишком большие документы, содержащие строки не-UTF-8 или использующие нераспознанные типы) перед отправкой чего-либо в базу данных.
Обновление документов
После сохранения документа в базе данных его можно изменить с помощью метода обновления. Обновление принимает два параметра: документ запроса, который находит документы для обновления, и документ модификатора, который описывает изменения, которые необходимо внести в найденные документы.
Обновление документа является атомарным: если два обновления происходят одновременно, будет применено то, которое достигнет сервера первым, а затем будет применено следующее. Таким образом, конфликтующие обновления могут быть безопасно отправлены в быстрой последовательности без каких-либо повреждений документов.
Замена документа
Самый простой тип обновления полностью заменяет соответствующий документ новым. Это может быть полезно при миграции схемы. Например, предположим, что мы вносим серьезные изменения в пользовательский документ, который выглядит следующим образом:
Мы хотим переместить поля "friends" и "enemies" в поддокумент "relationships". Мы можем изменить структуру документа в оболочке, а затем заменить версию базы данных обновлением:
Теперь выполнение findOne показывает, что структура документа была обновлена:
Распространенной ошибкой является сопоставление более одного документа с критериями, а затем создание дублирующего значения "_id" со вторым параметром. База данных на это выдаст ошибку, и никакие документы обновлены не будут.
Использование модификаторов
Обычно только определенные части документа должны быть обновлены. Вы можете обновить определенные поля в документе, используя атомарные модификаторы обновления. Модификаторы обновления – это специальные ключи, которые можно использовать для указания сложных операций обновления, таких как изменение, добавление или удаление ключей, и даже манипулирование массивами и внедренными документами.
Предположим, мы храним аналитику веб-сайта в коллекции и хотим увеличивать счетчик каждый раз, когда кто-то посещает страницу. Мы можем использовать модификаторы обновления, чтобы сделать это увеличение атомарно. Каждый URL и его количество просмотров страниц хранятся в документе, который выглядит следующим образом:
Каждый раз, когда кто-то посещает страницу, мы можем найти страницу по ее URL-адресу и использовать модификатор "$inc", чтобы увеличить значение ключа "pageviews":
Теперь, если мы проведем проверку, то увидим, что количество просмотров страниц увеличилось на один: