Семейства нереляционных хранилищ
Что это такое
Большинство нереляционных хранилищ укладывается в четыре семейства. Хранилище ключ-значение отображает ключ в непрозрачное значение, документное — ключ в структурированный документ, внутри которого умеет запрашивать, ширококолоночное — ключ в упорядоченный набор столбцов и построено под очень большие объёмы записи, а графовое хранит связи как объекты первого класса и обходит их напрямую. Каждое из них отказывается от чего-то, что даёт реляционная модель.
Почему это важно
Названия скрывают различия, которые важны. Два хранилища, оба называемые документными базами, могут полностью различаться тем, как они индексируют, поддерживают ли транзакции между документами и как распределяют данные. Если выбирать по категории, а не по конкретным гарантиям продукта, система рано или поздно не сможет выполнить операцию, которую приложение считает атомарной. Знание того, от чего отказывается каждое семейство, и позволяет задать вопрос «нужно ли это ещё кому-нибудь?» до того, как выбор сделан.
Ключевые идеи
Ключ-значение — самый маленький возможный интерфейс
Хранилище знает только, что ключ существует и у него есть значение; само значение непрозрачно. Поэтому оно чрезвычайно быстро и чрезвычайно просто, а любой запрос сверх выборки по ключу должно строить приложение. Состояние сессий, кэши и простые счётчики — естественное применение такого хранилища.
ключ: "session:8f2a" значение: непрозрачные байты
Документные хранилища добавляют структуру и вторичные индексы
Документ — самодостаточная вложенная запись, и хранилище может индексировать поля внутри неё и отвечать на запросы по ним. Это подходит данным, которые естественно читаются целиком, — профиль пользователя, заказ с его строками, — и убирает необходимость соединения для вложенной структуры. Произвольного реляционного запроса оно не даёт, а транзакции, которые охватывают несколько документов, либо отсутствуют, либо дороже записи одного документа.
Ширококолоночные хранилища оптимизированы под огромный объём записи
Данные разбиты по ключу и хранятся в отсортированном порядке внутри раздела, поэтому диапазон связанных строк читается за один проход, а добавления дешевы. Компромисс в том, что чтение эффективно только вдоль спроектированного порядка ключей, и запросы ad-hoc не самоцель. Временные ряды и данные событий — канонический случай.
Графовые хранилища делают обход примитивом
Узлы и рёбра хранятся напрямую, поэтому запрос, проходящий связи на несколько уровней вглубь, — короткое выражение, а не последовательность соединений. Компромисс в том, что хранилище плохо справляется с агрегатными запросами, которые реляционный движок делает хорошо; однако граф обычно дополняет основное хранилище, а не заменяет его.
Согласованность между узлами — самая трудная гарантия
Хранилища, распределяющие данные по машинам, должны выбирать между требованием согласия до подтверждения записи и немедленным подтверждением с распространением после. Первое медленнее и проще для рассуждений; второе быстрее и означает, что чтение может вернуть устаревшие данные. Что приемлемо, зависит от того, может ли приложение на миг выдержать чтение вчерашнего значения; для баланса счёта ответ отличается от ленты соцсети.
Язык запросов — это интерфейс, и он не переносим
Реляционные хранилища разделяют стандартный язык запросов, поэтому запрос, написанный для одного, часто работает на другом. У нереляционных хранилищ у каждого свой интерфейс, поэтому доступ приложения к данным оказывается привязан к продукту. Это настоящая цена внедрения, и она говорит за то, чтобы ограничить доступ небольшим слоем, а не размазывать его по коду.
Практика
Сопоставьте четыре нагрузки четырём семействам
Возьмите хранилище сессий, журнал событий с большим объёмом добавлений, профиль пользователя, читаемый целиком, и граф рекомендаций; назначьте каждому семейство с указанием причины.
Готово, когда: каждое назначение называет свойство, из-за которого три других не подошли, а не просто менее оптимальны.
Определите требование к согласованности
Для корзины покупок и для счётчика подписчиков скажите, может ли чтение вернуть значение, отставшее на миг, и почему.
Готово, когда: ответ для каждого обоснован тем, что заметил бы пользователь, а не тем, что хранилище делает по умолчанию.
Частые ошибки
- Выбор семейства по репутации, а не по гарантиям — два хранилища одной категории могут различаться транзакциями и индексацией; исправить: изучите конкретные гарантии продукта.
- Использование хранилища ключ-значение с надстройкой слоя запросов — механизмы, которых избегали, переизобретены хуже; исправить: используйте хранилище, умеющее запросы, если запросы нужны.
- Ожидание реляционных запросов от ширококолоночного хранилища — доступ эффективен только вдоль спроектированного порядка ключей; исправить: проектируйте ключ под те чтения, которые есть.
- Добавление графового хранилища для связей в один уровень — внешний ключ уже отвечает на это; исправить: оставьте граф для действительно глубокого обхода.
- Размазывание интерфейса хранилища по приложению — каждое обращение к данным привязано к продукту; исправить: держите доступ к данным за небольшим слоем.
Что читать дальше
- Когда реляционное хранилище плохо подходит (05-backend-data/13-nosql/01-when-a-relational-store-fits-badly) — несоответствия, на которые отвечают эти семейства.
- NoSQL (05-backend-data/13-nosql) — собирательная категория, которую образуют эти четыре семейства.
- MongoDB (05-backend-data/18-mongodb) — одно документное хранилище подробно, включая его гарантии.