Learning Platform
Глоссарий Troubleshooting
Урок 19.02 · 17 мин
Начальный
nosqlkey-valueredisaccess-patterns

Key-value: моделирование через структуру ключа

В прошлом уроке мы начали уходить из реляционного мира — разобрали документные базы. Теперь спустимся к самой простой модели хранения из существующих: key-value. Её представители — Redis, Amazon DynamoDB (в простейшем режиме), Memcached. Простая модель не означает простое моделирование. Наоборот: чем примитивнее хранилище, тем больше работы и дисциплины ложится на проектировщика. Здесь query-first мышление из прошлого урока доходит до предела.

Главная идея урока: в key-value хранилище моделирование данных — это дизайн структуры ключа. У вас нет таблиц, нет колонок, нет индексов по произвольным полям. Есть только ключи и значения. И вся ваша инженерная мысль уходит в то, как устроены ключи.


Что такое key-value хранилище

Key-value хранилище — это, по сути, гигантский словарь (как dict в Python или Map в JavaScript). Вы кладёте значение по ключу и достаёте значение по ключу. Всё.

SET user:1001:name        "Анна Петрова"
GET user:1001:name        -> "Анна Петрова"

SET session:abc123        "{user_id: 1001, expires: ...}"
GET session:abc123        -> "{user_id: 1001, expires: ...}"

Ключ — строка. Значение — что угодно: строка, число, сериализованный JSON, иногда более сложные структуры (Redis умеет списки, хеши, множества). Операции тоже сводятся к минимуму: положить по ключу (SET), достать по ключу (GET), удалить по ключу (DEL).

И вот критическое ограничение: запросить данные можно только по ключу. В SQL вы пишете WHERE city = 'Москва' — база найдёт нужные строки. В key-value так нельзя. Нет WHERE, нет «найди все значения, где внутри есть Москва». Если вы не знаете точный ключ — вы не можете достать значение. Это ограничение и есть отправная точка моделирования.

Key-value: доступ только по точному ключу
Знаю ключuser:1001:name — точный ключ известен
GET
Получаю значениеХранилище мгновенно возвращает значение по ключу
не знаю ключ
Поиск по содержимомуНельзя: нет WHERE, нет фильтра по полям значения

Моделирование начинается с access pattern

Раз достать данные можно только по точному ключу, моделирование строится строго наоборот по сравнению с реляционным. Не «какие у меня сущности и связи», а «какие запросы будет делать приложение». Это и есть access pattern — конкретный способ, которым приложение обращается к данным.

Порядок проектирования key-value хранилища:

  1. Выписать все access patterns: какие именно данные приложение будет читать и писать.
  2. Для каждого access pattern спроектировать ключ так, чтобы нужные данные доставались за один GET.
  3. Сложить значения по этим ключам.

Шаг 1 здесь — самый важный и самый недооценённый. В реляционной базе если вы забыли про какой-то запрос — не беда, напишете новый SELECT с новым WHERE. В key-value забытый access pattern означает, что нужных данных просто не достать — для них не спроектирован ключ. Поэтому в key-value сначала исчерпывающе собирают все паттерны доступа, и только потом проектируют ключи.


Дизайн структуры ключа

Ключ в key-value — не случайная строка. Это спроектированная структура, которая кодирует, что за данные внутри. Сложился отраслевой приём: ключ собирают из осмысленных частей через разделитель (обычно двоеточие).

<сущность>:<идентификатор>:<атрибут>

user:1001:profile          -> профиль пользователя 1001
user:1001:cart             -> корзина пользователя 1001
user:1001:orders           -> список id заказов пользователя 1001
order:5501:details         -> детали заказа 5501
product:SKU-100:stock      -> остаток товара SKU-100

Почему именно так? Структурированный ключ решает три задачи. Первая — детерминированность: зная сущность и id, приложение само соберёт нужный ключ по шаблону, без обращения к какому-либо каталогу. Чтобы достать корзину пользователя 1001, код строит user:1001:cart — и делает GET. Вторая — отсутствие коллизий: префикс сущности (user:, order:) разводит пространства имён, ключ пользователя и ключ заказа не столкнутся. Третья — группировка: общий префикс user:1001: собирает все данные одного пользователя под одним «зонтиком», что удобно для управления (например, удалить всё про пользователя по префиксу).

Ключевой навык — проектировать ключ под access pattern. Разберём на примере.

Access pattern: «показать корзину пользователя». Приложение знает user_id. Проектируем ключ user:{user_id}:cart, кладём туда сериализованную корзину. Запрос — один GET user:1001:cart.

Access pattern: «показать заказы пользователя за 2026 год». Знаем user_id и год. Проектируем ключ user:{user_id}:orders:{year}user:1001:orders:2026. Год в ключе позволяет достать заказы конкретного года напрямую, не перебирая все.

Один access pattern — один спроектированный ключ
Access patternКонкретный запрос приложения: показать корзину пользователя
проектируем ключ под этот запрос
Шаблон ключаuser:{user_id}:cart — строится детерминированно из известных приложению данных
один GET
Значение за одно чтениеНужные данные достаются одним обращением по ключу

Когда access pattern не ложится в один ключ

Иногда нужный запрос не покрывается одним ключом напрямую. Классический приём — завести дополнительный ключ-индекс.

Пример. Access pattern: «найти пользователя по email» (при логине). Основные данные лежат по user:{user_id}:profile, но при логине известен email, а не id. Решение — второй ключ, который отображает email в id:

# основной ключ — данные по id
user:1001:profile           -> {name: "Анна", email: "[email protected]", ...}

# ключ-индекс — email отображается в id
email_index:[email protected]    -> "1001"

# логин в два шага:
# 1) GET email_index:[email protected]     -> "1001"
# 2) GET user:1001:profile            -> данные пользователя

Вы вручную построили то, что в реляционной базе делал бы индекс по столбцу email. В key-value нет автоматических индексов по полям — если нужен доступ по email, проектировщик заводит ключ-индекс сам. Это плата за простоту модели: гибкость доступа не дана из коробки, её строят руками, ключ за ключом, под каждый известный заранее access pattern.

WARNING

Главный риск key-value моделирования — забытый access pattern. Если паттерн доступа не учтён при проектировании, под него нет ключа, и данные физически не достать без полного перебора хранилища (а это в проде недопустимо). Поэтому в key-value исчерпывающий сбор всех access patterns ДО проектирования — не формальность, а критический шаг. Меняется набор паттернов — приходится добавлять ключи и часто перекладывать данные.

Redis в DE-пайплайне — когда и зачем key-value рядом с аналитической БД

Почему вообще key-value: цена за простоту

Возникает резонный вопрос: зачем мириться с такими ограничениями? Что даёт key-value взамен утраченной гибкости запросов?

Ответ — скорость и масштаб. Поскольку доступ всегда по точному ключу, хранилище не выполняет ни планирование запроса, ни сканирование, ни JOIN. GET по ключу — это, по сути, прямой просмотр в хеш-таблице, операция почти константного времени. Key-value хранилища обрабатывают огромный поток операций с очень малой задержкой. И они легко масштабируются: ключи распределяются по узлам по хешу (вспомните hash из модуля про Data Vault), нагрузка равномерно растекается.

Отсюда — типичные применения key-value: кэш (product:SKU-100:price с коротким временем жизни), пользовательские сессии (session:{token}), счётчики, очереди, профили под высоконагруженный доступ. Везде, где паттерн доступа простой и предсказуемый, а нужны экстремальная скорость и масштаб.

Это и есть осознанный компромисс. Реляционная база даёт гибкость запросов ценой более сложной обработки. Key-value отдаёт гибкость и взамен получает скорость и масштаб. Выбор хранилища — это выбор того, чем вы готовы пожертвовать; и моделирование под key-value есть искусство уложить все нужные access patterns в структуру ключей. В следующем уроке посмотрим на wide-column базы — следующий шаг сложности, где этот query-first подход развивается дальше.


Попробуй сам

Возьмите приложение — например, онлайн-кинотеатр с сущностями: пользователь, фильм, история просмотров, список «посмотреть позже».

  1. Выпишите 5-6 access patterns: «показать профиль пользователя», «показать список посмотреть позже», «продолжить просмотр фильма» и т.д. Старайтесь не пропустить ни одного — это критический шаг.
  2. Для каждого access pattern спроектируйте шаблон ключа из осмысленных частей через двоеточие.
  3. Один access pattern сделайте таким, что не ложится в один ключ напрямую (например, «найти пользователя по номеру телефона»). Спроектируйте для него ключ-индекс.
  4. Объясните, что произойдёт, если через полгода появится новый access pattern, который вы не предусмотрели. Почему в key-value это болезненнее, чем в SQL?

Проверка знанийKnowledge check
Почему в key-value хранилище моделирование данных сводится к дизайну структуры ключа, и почему исчерпывающий сбор access patterns до проектирования — критический шаг?
ОтветAnswer
Key-value хранилище — это, по сути, гигантский словарь: вы кладёте значение по ключу и достаёте значение по ключу, и больше ничего. Критическое ограничение: запросить данные можно только по точному ключу — нет WHERE, нет поиска по содержимому значения, нет индексов по произвольным полям. Поэтому вся инженерная работа уходит в дизайн структуры ключа: ключ собирают из осмысленных частей через разделитель (user:1001:cart), чтобы приложение могло детерминированно построить нужный ключ из известных ему данных, чтобы префиксы сущностей разводили пространства имён без коллизий и группировали связанные данные. Моделирование строится строго от access patterns — конкретных способов, которыми приложение обращается к данным: сначала выписывают все паттерны, потом для каждого проектируют ключ так, чтобы нужные данные доставались за один GET. Исчерпывающий сбор access patterns ДО проектирования критичен, потому что забытый паттерн означает, что под него нет ключа, и нужные данные физически невозможно достать без полного перебора хранилища, недопустимого в проде. В реляционной базе забытый запрос не беда — пишется новый SELECT с новым WHERE; в key-value забытый access pattern — это отсутствие ключа и недоступность данных. Если access pattern не ложится в один ключ (поиск пользователя по email при логине), заводят дополнительный ключ-индекс вручную — то, что в SQL делал бы автоматический индекс по столбцу.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что является сутью моделирования данных в key-value хранилище?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5