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.
- Источники таблицы: MongoDB — Community Edition licensing · MongoDB — BSON document size limit · Redis — Licenses · Elastic — Licensing FAQ · AWS — DynamoDB constraints
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 эвристик |