Преимущества и подводные камни Azure Cosmos DB
Немало баз данных на сегодняшний день стремятся сделать всё, чтобы обеспечить высокую производительность, масштабируемость и доступность, при этом минимизируя сложность и стоимость поддержки. Azure Cosmos DB — отличный пример СУБД, которая легко может обеспечить эти качества. Данная статья описывает её возможности вместе с ограничениями, которые могут быть неочевидными с первого взгляда и при этом стать серьезной проблемой в будущем, если их не учесть при проектировании системы.
Далее в статье некоторые факты будут отмечены специальными символами:
Cosmos DB поддерживает несколько API: SQL, Cassandra, MongoDB, Gremlin, Table. Здесь под SQL подразумевается документ-ориентированный API, который раньше назывался DocumentDB, и он значительно отличается от привычных нам реляционных баз данных. Данная статья основана на опыте работы с SQL API. Хочу обратить внимание, что тип API выбирается при создании экземпляра хранилища, работать с данными одного и того же экземпляра через разные API не получится.
Документная модель Cosmos DB хранит данные в контейнерах (containers), состоящих из элементов (items). Ранее в документации они назывались коллекциями и документами соответственно. Все настройки масштабирования, пропускной способности, индексирования указываются на уровне контейнера (за некоторыми исключениями, о которых мы поговорим позже). База данных, по большому счету — именованное объединение контейнеров.
И здесь сразу стоит сказать о первом ограничении:
Масштабирование «на лету»
Cosmos DB позволяет управлять метрикой производительности (пропускной способностью) для каждого контейнера индивидуально.
Пропускная способность измеряется в единицах запроса в секунду (Request Units per second или сокращенно RU/sec). Примерным эквивалентом единицы запроса можно считать чтение элемента размером 1 Кб по его идентификатору. Например, необходимая пропускная способность контейнера, позволяющего делать 500 чтений и 100 записей однокилобайтных элементов в секунду будет примерно равна 500 * 1 + 100 * 5 = 1000 RU. Однако, в общем случае, с точностью расчитать требуемую пропускную способность пратически невозможно, так как сложность запросов и размеры элементов могут быть очень разными.
Любая операция, выполняемая базой данных имеет свою «цену» в терминах RU. Указанная на контейнере пропускная способность — это лимит, который Cosmos DB разрешает «потратить» в секунду. База отслеживает суммарную «цену» запросов в пределах секунды и если лимит уже исчерпан, последующие запросы не принимаются к исполнению, пока сумма за секунду не вернется в значение меньше указанного лимита.
Минимально возможная величина пропускной способности для контейнера равна 400 RU и обойдется примерно в 25 долларов в месяц. Стоимость линейно растет с увеличением RU — контейнер с 800 RU будет уже стоить порядка 50 долларов в месяц.
Партиционирование для достижения бесконечной масштабируемости
У Cosmos DB раньше было две опции для контейнеров: с разделами (partitioned) и без (non-partitioned). Контейнеры без разделов были ограничены максимумом пропускной способности в 10000 RU и размером в 10 гигабайт. На данный момент все контейнеры могут иметь разделы, поэтому при их создании необходимо указывать ключ партиционирования.
Важным моментом является понимание разницы между логическими и физическими разделами: логический состоит из набора элементов, имеющих одинаковое значение ключа партиционирования, в то время как физический раздел — «вычислительная единица» Cosmos DB, узел ее физической инфраструктуры, который может обрабатывать несколько логических разделов. В зависимости от объема данных и распределения записей по ключу партиционирования, система может создавать новые физические разделы и перераспределять между ними логические.
Автоиндексация
Вместо создания индексов для отдельных полей или их сочетаний, Cosmos DB позволяет настроить политику индексирования для путей внутри объекта. Политика представляет собой набор атрибутов: какие пути включить в индексацию, какие исключить, какие типы индексов использовать и т.д.
Интересным моментом является то, что в отличие от большинства других СУБД, Cosmos DB использует инвертированный индекс (inverted index) вместо классического B-дерева (B-tree), что делает его очень эффективным при поиске по нескольким критериям и не требует создания составных индексов для поиска по нескольким полям. Больше деталей на эту тему можно найти в статье по этой ссылке.
Отслеживание изменений
Отслеживание изменений в Cosmos DB возможно благодаря механизму, который называется Change Feed. Он возвращает документы, измененные с определенного момента времени, в том порядке, в котором они были изменены. Этим начальным моментом времени можно гибко управлять: им может быть либо момент инициализации самого потока изменений, либо фиксированная временная метка, либо момент создания контейнера.
Изменения могут быть обработаны асинхронно и инкрементально, а результат может быть распределен между одним и более потребителями для параллельной обработки. Это все дает очень большую гибкость в различных интеграционных сценариях.
Хранимые процедуры и транзакции
Cosmos DB поддерживает хранимые процедуры и триггеры, написанные на JavaScript.
Выполнение запросов
Хоть Microsoft и называет документный API “SQL”, его язык всего лишь похож на SQL и имеет много отличий от того, что мы привыкли видеть в реляционных базах.
Больше подсказок на тему запросов можно найти в шпаргалках, опубликованных Microsoft.
Другие полезные возможности
-
Время жизни элементов — может быть указано либо по умолчанию на уровне контейнера, либо приложение может установить время жизни для каждого элемента индивидуально.
- Пять уровней целостности: bounded staleness, session (по умолчанию), consistent prefix, eventual.
-
Уникальные ключи (обратите внимание, что для изменения структуры уникального ключа придется пересоздать контейнер).
-
Оптимистическая блокировка — каждый элемент имеет специальное поле "_etag", обновляемое самой СУБД. Код приложения во время обновления элемента может указать условие — разрешить запись только в случае если значение поле "_etag" в объекте, переданном приложением, равно значению, сохраненному для этого элемента в базе.
-
Гео-репликация — очень легко настраивается на любое количество регионов. В конфигурации с одним «мастером» в любой момент можно переключить основной регион, или это переключение будет автоматическим в случае сбоя на уровне датацентра. Клиент из SDK автоматически реагирует на эти переключения, не требуя от разработчиков никаких дополнительных действий для обработки подобных ситуаций.
-
Шифрование данных
-
Запись в нескольких регионах — позволяет масштабировать операции записи. Следует помнить, что наличие нескольких копий данных, разрешающих запись, практически всегда подразумевает возможные конфликты: один и тот же элемент изменён в двух регионах одновременно, несколько элементов с одинаковым первичным ключом добавлены в разных регионах и т.д. К счастью, Cosmos DB предоставляет два способа разрешения конфликтов: автоматический (последняя операция записи побеждает) или «свой вариант», в котором можно реализовать алгоритм, подходящий под ваши условия.
Как можно увидеть из описанного выше, Azure Cosmos DB имеет большое количество преимуществ, которые делают её хорошим выбором для различных проектов. Но нет ничего идеального, и некоторые ограничения могут быть серьезным препятствием для применения этой технологии: если вам нужны транзакции, состоящие из действий над несколькими контейнерами, или нужны длинные транзакции, включающие в себя сотни и тысячи объектов, если данные не могут быть эффективно партиционированы и при этом могут выйти за пределы 10 Гб и т.д. Если ни одно из ограничений, упомянутых в этой статье, не кажется большой проблемой для вашего проекта — имеет смысл рассмотреть Azure Cosmos DB.
Выражаю благодарность l0ndra за помощь в подготовке статьи.
P.S. Эту статью я уже публиковал ранее в английском варианте, но на другом ресурсе.