Перейти к содержанию

D-01 · Выбор хранилища#

  • Версия: 2.2 · 2026-09-22 · по мастер-шаблону v6.0
  • Известно также как: выбор БД, SQL или NoSQL, persistence choice, database selection
  • Этап: W3 · Данные: модель и хранилище
  • Стадия: гринфилд · работающая — нулевой вариант различается: на работающей «расширить имеющееся», на гринфилде его нет
  • Профиль: дорого изменить · высокое влияние → полная форма
  • Не решает: пиковую нагрузку на чтение — это W5 и D-03; форму данных внутри сервиса — это карточка DATA-03; аналитический контур — DATA-15; надо ли вообще заводить второе хранилище — это D-07; что отдавать, когда выбранное хранилище недоступно, — D-06; как переехать в него, не останавливаясь, — D-08; можно ли измерителю обещания делить хранилище с измеряемым — D-10
  • Не путать с: D-03 — там выбирают, чем снять чтения, а не где лежит истина
  • Карточки: DATA-01 выбор хранилища · DATA-02 специализированные хранилища · DATA-03 моделирование от паттернов доступа · DATA-05 индексы · DATA-15 OLTP и OLAP

1. Что выяснить#

Вопрос Заполняет ось
Какие 3–5 запросов дают основную нагрузку: по какому ключу, сколько строк возвращают, с какой частотой, с каким требованием к задержке? Паттерны доступа, гибкость запросов
Каково соотношение чтения и записи у этого хранилища и профиль нагрузки — ровный, пиковый, сезонный? Профиль нагрузки
Каков объём сейчас, ожидаемый через год, и сколько данных нужно хранить — и задан он или выбирается нами? Если границы данных уже выбраны — по какому критерию, и не пора ли пересчитать? Объём и рост
Что обязано меняться атомарно и какие инварианты нельзя нарушать? Транзакционные границы
Какая свежесть данных допустима для каждого типа чтения? Требуемая согласованность
Кто будет это эксплуатировать и есть ли управляемая версия там, где мы размещаемся? Операционная стоимость
Есть ли ограничения по лицензии, поставщику или юрисдикции хранения? Дисквалификаторы

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

Два вопроса требуют осторожности#

Соотношение чтений к записям у системы и у отдельного хранилища — разные числа, и они бывают перевёрнуты. Спрашивать надо про это хранилище. Системное соотношение приводится отдельно и помечается как контекст, а не как ответ: любая схема, переносящая работу с чтения на запись — материализация, денормализация, предрасчёт, — даёт хранилищу картину, обратную системной. На прогоне E-02 в системе было сто чтений на запись, а у выбираемого хранилища — 12 000 чтений против 17 400 вставок в пике. Прочитать системное число как своё значит настроить хранилище, мониторинг и алерты вокруг чтения там, где профиль пишущий.

Объём бывает не данностью, а параметром. Если он зависит от нашего решения — длина материализованной ленты, глубина хранимой истории, срок жизни записи, — сначала назвать параметр и метрику, которая его задаёт, и лишь потом заполнять ось. Пока параметр не выбран, ячейка объёма — это [нет данных], а не оценка: в E-02 при длине ленты 1 000 идентификаторов выигрывал один кандидат, при 200 — другой, а длину задала выгрузка метрики за час работы.

Третий случай того же рода — параметр, выбранный до этого документа. Границы данных (горячее и холодное, срок жизни, глубина истории) нередко выбираются раньше, в D-07, — там, где решается форма разделения. Но критерий для них даёт первый вопрос этого раздела: какие запросы дают основную нагрузку. D-07 предупреждает, что такая граница «подбирается на глаз и уезжает», и на прогоне E-03 она уехала на следующем же этапе: выбранная по периоду инвентаризации, она отправляла две трети обращений в архив.

Предупреждение не помогает, потому что в момент выбора нужного числа ещё нет. Поэтому проверка стоит здесь: увидев уже выбранную границу, спросить, каким критерием она задана, и пересчитать по запросам. Это дешевле, чем кажется, и всегда дешевле, чем переносить её после.

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

2. Оси и дисквалификаторы#

Оси сравнения. Форма данных · паттерны доступа · профиль нагрузки · объём и рост · транзакционные границы · требуемая согласованность · гибкость запросов · операционная стоимость · стоимость владения на единицу нагрузки. Что писать в каждую — в таблице раздела 3.

Дисквалификаторы — проверяются до сравнения:

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

Две силы удерживают данные там, где они лежат#

Этот документ отвечает на вопрос «где лежит», предполагая, что отдельное хранилище нужно. Само это предположение стоит проверить, и проверяют его две встречные силы (Ford, Richards, гл. 6):

Сила Что спросить Оплатить можно так
Отношения в данных Какие внешние ключи, соединения и представления связывают эти данные с теми, что останутся на месте? Денормализация плюс сверка · соединение в коде вместо соединения в базе · оставить данные там, где они есть
Транзакции Требуется ли атомарность между этими данными и остальными? Не оплачивается: это дисквалификатор «не даёт нужной транзакционной границы» с другой стороны

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

Полное решение о декомпозиции данных — D-07. Вопрос «делятся ли данные и по каким критериям» шире выбора продукта: у него шесть сил разделения и три интеграции. Две силы выше — тот минимум, который нельзя пропустить, даже если до D-07 дело не дошло.

3. Матрица оценки#

Строки — все девять осей, колонки — оставшиеся после дисквалификации варианты. В ячейках факты с пометкой источника, не баллы.

Колонка «что записывать» остаётся в матрице и при заполнении: документ открывают в том числе те, кто видит его впервые, и вопрос «а что сюда писать» не должен требовать чтения остального.

Пример условный. Он показывает форму ячейки, а не факты о продуктах: числа выдуманы, X и Y — места под свои, пометки [док] и [замер] стоят образцом. Проверенные факты о конкретных продуктах — только в разделе 4 и только со ссылкой.

Ось Что записывать в ячейку Реляционное — пример Документное — пример
Форма данных Как сущность ложится на модель и что при этом ломается Заказ — строка плюс индексируемое JSON-поле под переменные атрибуты [док] Заказ с позициями — один документ, вложенность 2 уровня [док]
Паттерны доступа Выражается ли каждый запрос из раздела 1 и каким индексом Все пять выражаются; №3 требует составного индекса [док] №3 требует обхода всех партиций — нет подходящего ключа [док]
Профиль нагрузки Число на нашем профиле, с датой замера — не из презентации поставщика 8 000 записей/с на трёх узлах [замер 2026-09-10] [нет данных] — не замеряли
Объём и рост Сколько влезает до того, как придётся менять топологию До единиц ТБ на узел, дальше шардирование [сторонний] Рост прозрачен, потолок — бюджет, а не топология [док]
Транзакционные границы Что именно меняется атомарно — в терминах наших сущностей, не «ACID есть» Заказ, позиции и остаток — в одной транзакции [док] Только в пределах одного документа; остаток — отдельно [док]
Требуемая согласованность Режим по умолчанию и цена более строгого Строгая на первичном узле; на репликах отставание до N мс [замер] По умолчанию итоговая; строгое чтение дороже [док]
Гибкость запросов Что можно спросить, не меняя схему, ключ или индекс Новый отчёт — запросом, без миграции [док] Новый паттерн — новый индекс или перезаливка [док]
Операционная стоимость Кто дежурит, что команда уже умеет, чему придётся учиться Управляемая версия, дежурит платформенная команда Эксплуатируем сами, опыта в команде нет
Стоимость владения на единицу нагрузки Деньги на миллион операций или на терабайт в месяц — не общей суммой Резерв под базовую нагрузку: X за 1 млн операций при нашем объёме [замер] Оплата по запросам: Y за 1 млн; при ровной нагрузке дороже резерва [док]

Ячейка [нет данных] — это не пустое место, а результат: она говорит, какой замер нужно сделать, прежде чем решать. Строка, целиком состоящая из [нет данных], — сигнал, что решение принимать рано.

Нулевой вариант [стадия: гринфилд]. Если хранилище уже есть, «оставить текущее и расширить его» — обязательная колонка матрицы, а не подразумеваемый фон: без неё сравниваются способы переехать, а вопрос, надо ли переезжать, не задаётся. На гринфилде расширять нечего, и правило не срабатывает: колонки «оставить как есть» не существует. Её место занимает реляционная БД по умолчанию — по W3 отклонение от неё требует обоснования, а не наоборот. Колонка при этом остаётся обязательной: пропасть должен вариант, а не вопрос «а надо ли вообще заводить второе».

Число строк не фиксировано. Ось, по которой варианты не различаются, вычёркивается: она не влияет на выбор и создаёт только видимость тщательности. Ось, которой не хватает, добавляется вместе с вопросом в раздел 1 — ось без своего вопроса нечем заполнить, и она останется пустой.

4. Типовые решения · снято 2026-09-16#

Факты проверены по документации поставщиков на указанную дату. Лицензии в этом сегменте менялись трижды за последние годы — перед использованием таблицы проверьте дату съёмки.

Решение Класс Лицензия Жёсткие ограничения Статус у нас
PostgreSQL Реляционное PostgreSQL License [не проверено] — заполняется в проекте
MySQL Реляционное GPLv2 с коммерческой опцией [не проверено] —
MongoDB Community Документное SSPL v1.0 для версий после 16.10.2018; не одобрена OSI [док] Документ ≤ 16 MiB [док]
Redis 8.0+ Ключ-значение, структуры На выбор: RSALv2, SSPLv1 или AGPLv3. До 7.2 — BSD-3; 7.4–7.8 — только RSALv2/SSPLv1 [док] —
Valkey Ключ-значение Форк Redis после смены лицензии 2024 [не проверено] —
Amazon DynamoDB Управляемое ключ-значение Проприетарное, только в AWS Элемент ≤ 400 КБ; ключ раздела ≤ 2048 байт; ключ сортировки ≤ 1024 байта [док]
Elasticsearch Поисковое На выбор: ELv2, SSPL или AGPLv3 (AGPL добавлена в сентябре 2024, с версии 8.16) [док] Не источник истины (DATA-13)
OpenSearch Поисковое Apache 2.0, форк Elasticsearch [не проверено] То же
Apache Cassandra Wide-column Apache 2.0 [не проверено] Запросы только по заранее выбранному ключу
ClickHouse Колоночное Apache 2.0 [не проверено] Плохо для точечных обновлений

Строки с пометкой [не проверено] заполнены по общим сведениям и требуют сверки с первоисточником перед использованием как дисквалификатора. Пометка оставлена намеренно: она честнее, чем видимость проверенной таблицы.

Колонка «статус у нас» заполняется в проекте одним из трёх значений: используем · не используем, потому что… · не рассматривали. Второе — самое ценное: без него отвергнутый инструмент предлагают заново каждые полгода. У записи «не используем» обязательны дата и условие пересмотра.

Начиная примерно с третьей-четвёртой такой записи они выносятся из документов решений в общий реестр организационных ограничений — иначе «Mongo не используем» окажется написанным в одном документе и проигнорированным в другом. Порог низкий не случайно: на прогоне E-01 четыре записи набрались после первого же ADR.

5. Эвристики#

Ничья по осям. Выбрать обратимое. При равной обратимости — то, что уже есть в инфраструктуре: второй тип хранилища стоит дороже, чем кажется при выборе, — бэкапы, мониторинг, обновления, экспертиза и дежурство удваиваются.

Нет данных о нагрузке. Реляционная БД и возврат к решению после первых замеров. Записать, какой именно замер снимет вопрос: обычно это профиль запросов на реальных данных и объём через полгода.

Спор «один инструмент или несколько». Сначала расширение основного: JSON-поля, полнотекстовый поиск, расширение для векторов. Отдельная система вводится, когда упирается с конкретным числом, а не по ожиданию, что упрётся (DATA-02).

Давление «возьмём как у них». Запросить их числа и сравнить со своими. Разница обычно на порядки, и решение не переносится (CASE-13).

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

6. Запись в ADR#

Поле ADR Чем заполняется отсюда
Контекст Ответы на семь вопросов раздела 1 — числами
Дисквалификаторы Какие варианты отпали без сравнения и по какому условию
Рассмотренные варианты Оставшиеся после дисквалификации — заполненная матрица целиком, снимком
Решение Выбранный вариант
Обоснование Какие оси перевесили; если была ничья — какая эвристика применена
Последствия Что становится дороже, что теряем
Условия пересмотра Раздел 7
Метод D-01 v<версия> · рыночный снимок <дата раздела 4> · шаблон ADR v<версия> — зачем цепочка целиком

Матрица копируется в ADR снимком, а не прикладывается ссылкой. ADR отвечает на вопрос «почему так решили тогда» — заполненная матрица и есть те условия. Ссылка на живой документ означает, что через полгода обоснование молча изменится вместе с ним: рынок пересняли, лицензия другая, ячейка [нет данных] заполнилась — и ADR теперь ссылается на картину, которой в момент решения не было. Поэтому в ADR попадает копия матрицы целиком, с датой заполнения.

Ссылкой прикладывается только метод — этот документ и дата рыночного снимка, по которой шли. Метод живой, и меняться он должен.

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

7. Условия пересмотра#

  • Объём или нагрузка выросли на порядок относительно оценок из W1.
  • Профиль сменился с читающего на пишущий или наоборот.
  • Появилось требование, невыразимое в текущей модели данных.
  • Изменилась лицензия или условия поставщика — за последние годы это происходило с Redis, Elasticsearch и MongoDB; для этого сегмента пункт не теоретический.
  • Изменился состав команды или модель эксплуатации.

Проверка перед тем, как считать решение принятым — пять пунктов в шаблоне.

История версий#

Версия Дата Изменение
2.2 2026-09-22 MINOR, правка Р9.3·A плана ревизии. Нулевой вариант получил пометку [стадия: гринфилд]: «оставить текущее и расширить» там не существует, его место занимает реляционная БД по умолчанию. Прежде это было сказано прозой в конце абзаца — теперь стоит на самом правиле. Добавлено, что исчезает вариант, а не вопрос: «надо ли вообще заводить второе» задаётся и на гринфилде. Старым ADR перепроверять нечего: содержание правила не изменилось
2.1 2026-09-18 MINOR, по итогам прогона E-03. Третий вопрос раздела 1 дополнен проверкой уже выбранных границ данных: каким критерием заданы и не пора ли пересчитать по запросам. Ловушка поставлена там, где появляется число, — предупреждение в D-07 не срабатывает, потому что в момент выбора числа ещё нет
2.0 2026-09-17 MAJOR, по итогам прогона E-02. Вопрос 2 спрашивает соотношение у этого хранилища, а не у системы: при материализации и любом переносе работы на запись они перевёрнуты. Вопрос 3 требует различать, задан объём или выбирается нами; во втором случае сначала параметр и метрика, потом ось. Старым ADR: проверить, не системное ли соотношение записано в профиль нагрузки, и не является ли объём следствием параметра, который никто не выбирал осознанно — в E-02 выбор параметра изменил исход
1.1 2026-09-16 MINOR, по итогам прогона E-01: порог выноса в реестр ограничений снижен с десяти записей до трёх-четырёх; в шапку добавлены «Не решает» и «Не путать с». Метод не изменился, старым ADR перепроверять нечего
1.0 2026-09-16 Первая версия: 7 вопросов, 9 осей, 6 дисквалификаторов, рыночный слой со снимком 2026-09-16, 5 эвристик