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

Затем в проект вносится шардинг, но исходный конфликт транзакционной и аналитической нагрузки остаётся.
Ошибка закладывается до выбора СУБД. Одна база получает транзакционные записи, сложные поисковые запросы, отчётность, интеграционные события и долгие вычисления. Каждый поток выглядит допустимым по отдельности. Вместе они нарушают бюджет задержки, расходуют ресурсы и усложняют восстановление.
Архитектура базы данных должна следовать из профиля нагрузки, требований к согласованности и допустимых последствий отказа. Название вендора здесь вторично.
Профиль нагрузки: разделяем OLTP, OLAP и гибридные сценарии
OLTP обслуживает частые точечные операции: создание заказа, изменение остатка, чтение профиля, обновление платежного статуса. Приоритеты здесь следующие:
- предсказуемая задержка одной операции;
- корректность транзакции;
- работа с одной записью или небольшим набором строк;
- изоляция конкурентных изменений;
- восстановление по RPO и RTO.
OLAP работает с крупными срезами данных. Здесь преобладают агрегации, группировки, соединения, временные ряды и полнотекстовый поиск. Рабочая нагрузка обычно состоит из чтений, сканирования диапазонов и пакетного обновления витрин.
| Параметр | Транзакционный контур | Аналитический контур |
|---|---|---|
| Типичная операция | Изменение или чтение небольшого числа записей | Агрегация и чтение больших диапазонов |
| Основная метрика | TPS, задержка операции, число конфликтов | Объём обрабатываемых данных, свежесть витрины, производительность запроса |
| Согласованность | Обычно требуется ACID на уровне операции или бизнес-транзакции | Часто допустима-фиксация или согласованность на уровне витрины |
| Основной конфликт | Блокировки, длинные транзакции, перегрузка пула соединений | Сканирование данных, конкуренция с OLTP за кэш, I/O |
| Масштабирование | Вертикальное расширение, репликация чтения, затем шардинг | Распределённое хранение, колоночное хранение, отдельные вычислительные ресурсы |
HTAP-сценарий возникает, когда аналитика должна работать с актуальными транзакционными данными. Он требует явного определения допустимой задержки. Передача каждой записи в аналитический контур возможна, но не означает мгновенную согласованность: сеть, буферизация, вычисление витрины и запись в хранилище добавляют задержку.
До выбора технологии нужно собрать измеримый профиль:
1. Структура запросов. Доли чтения и записи, частота операций, размер возвращаемых данных, количество соединений между таблицами.
2. Характер данных. Объём таблиц, кардинальность значений, перекос ключей, наличие горячих записей, глубина истории.
3. Требования к задержке. Значения p95 и p99, допустимая доля таймаутов, поведение при всплесках нагрузки.
4. Требования к доступности. RPO, RTO, допустимое окно потери данных и простоя после отказа.
5. Темпы роста. Увеличение объёма, числа пользователей, числа сервисов и географического распределения.
TPS, QPS и RPS нельзя складывать в одну метрику. Первый считает завершённые транзакции, второй — запросы к базе, третий обычно относится к HTTP-запросам приложения, который может порождать несколько обращений к СУБД. Для сравнения СУБД нужен один набор операций, зафиксированный в одинаковых условиях.
ACID, BASE и CAP: фиксируем реальные компромиссы
ACID описывает свойства транзакции:
- Atomicity — транзакция применяется целиком либо не применяется.
- Consistency — изменение не нарушает правила целостности схемы и бизнес-инварианты.
- Isolation — конкурентные транзакции наблюдают согласованное состояние согласно выбранному уровню изоляции.
- Durability — подтверждённое изменение сохраняется после сбоя.
Само наличие ACID ещё не задаёт уровень изоляции. Требования serializable, repeatable read и read committed дают разные профили блокировок и конфликтов. Уровень изоляции следует указывать вместе с бизнес-операцией.
CAP относится к распределённым системам во время сетевого разделения. Концепция была сформулирована Эриком Брюером в 2000 году и формализирована Сетом Гилбертом и Нэнси Линч в 2002 году. При разрыве сети система выбирает между двумя свойствами:
- Consistency — все запросы видят согласованное актуальное состояние.
- Availability — каждый запрос к доступной части системы получает ответ.
- Partition tolerance — система продолжает работать при разделении сети.
Короткая формула «выбрать два из трёх» скрывает важное условие. Разделение уже произошло. CAP не описывает задержку при нормальной работе, стоимость согласованных транзакций или выбор между SQL и NoSQL. Кроме того, Consistency в ACID и Consistency в CAP имеют разный смысл. В ACID речь идёт о правилах целостности и изоляции. В CAP — о линеаризуемости или одинаковом состоянии узлов в один момент.
BASE используют как архитектурную модель для систем, допускающих временную рассинхронизацию: basically available, soft state, eventual consistency. Она подходит для счётчиков, каталогов, рекомендаций и других операций, где временная задержка обновления не разрушает бизнес-инвариант.
Слабая согласованность требует процесса компенсации ошибок. Финальная согласованность без сближения состояний превращается в постоянный расхождение данных.
Рабочий порядок выбора выглядит так:
| Бизнес-инвариант | Допустимая модель | Обязательный механизм |
|---|---|---|
| Списание и зачисление средств, остатки, заказы | Строгая согласованность в пределах операции | Транзакции, блокировки или сериализуемое исполнение |
| События доставки и статус обработки | Идемпотентность, ограниченная задержка, компенсация | Дедупликация, повторная доставка, reconciliation |
| Счётчики показов, ленты, рекомендации | Eventual consistency | Асинхронное распространение, версионность, сближение |
| Полнотекстовый поиск по транзакционным данным | Слабая свежесть либо отдельная индексирующая копия | Индексатор, обновление индекса, контроль задержки |
До распределения данных по узлам полезно установить инварианты. Например, перемещение средств между счетами требует атомарности, а обновление рекомендаций может выполняться с задержкой. Эти решения влияют на схему, протоколы обмена и допустимую архитектуру.
CDC: переносим изменения без тяжёлых выгрузок
Прямые периодические выгрузки из рабочей базы создают две проблемы. Они нагружают источник полными проходами по таблицам и создают окно неактуальности. Для больших данных этот быстро выходит за допустимый freshness SLO.
Change Data Capture, или CDC, извлекает изменения из журнала транзакций и передаёт их в аналитический контур. Обычная цепочка состоит из пяти частей:
1. Источник фиксирует изменение в журнале транзакций.
2. CDC-коннектор читает журнал и преобразует записи в события.
3. Поток событий буферизуется и разделяется по ключу обработки.
4. Потребитель обновляет витрину или хранилище.
5. Отдельный процесс сверяет контрольные суммы и aggregates.
Такой контур снижает нагрузку от полного сканирования источника, сохраняя свежесть, близкую к времени изменения. CDC не даёт нулевой цены: коннектор использует CPU, память, дисковый I/O и сетевой трафик. На тяжёлых транзакциях он способен конкурировать с прикладным запросами за ресурсы.
Существуют ограничения, которые CDC не устраняет:
- порядок событий зависит от ключа и конфигурации конвейера;
- повторная доставка требует идемпотентного потребителя;
- изменение схемы может сломать контракт события;
- историческая догрузка данных и текущий поток требуют разных процедур;
- удаление и обновление строки не всегда равнозначны событию бизнес-процесса.
Для бизнес-события, которое должно создаваться вместе с изменением строки, применяется транзакционный outbox. Событие записывается в ту же транзакцию, затем CDC публикует его downstream-системам. Это устраняет окно между коммитом бизнес-операции и публикацией сообщения.
Потребитель CDC должен принимать повторные события. Практический механизм — ключ идемпотентности с операцией upsert, журнал обработанных событий либо версионная проверка. Переход на exactly-once требует анализа границ транзакций CDC, брокера и целевой базы. Декларация режима в документации коннектора без проверки сценария отказа недостаточна.
Для аналитического контура нужны отдельные метрики: задержка CDC, задержка витрины, число отставших событий, частота конфликтов и расхождения при сверке. Freshness без контроля целостности превращается в видимый сбой.
Оценка производительности: бенчмарк должен воспроизводить профиль
Универсального значения RPS или QPS для СУБД не существует. Результат меняется под действием типа запросов, размера строк, схемы индексов, объёма данных, уровня конкуренции и оборудования. Поэтому сравнивать две системы по рекламному числу операций в секунду бессмысленно.
Бенчмарк начинается с трассы реальных запросов или её обезличенной реконструкции. В наборе должны учитываться:
- распределение операций по частоте;
- размер входных и выходных данных;
- число параметров запроса;
- количество и порядок обращений к индексам;
- характер конкурентных записей;
- работа с кэшем;
- пакетные и фоновые процессы;
- периоды низкой, пиковой и аварийной нагрузки.
Результат измеряется по нескольким осям. p95 показывает типичную задержку хвоста, p99 выявляет деградацию, которую средний показатель скрывает. TPS, QPS или RPS показывают пропускную способность. Число ошибок, таймаутов и отменённых запросов нельзя отделять от скорости. Следует фиксировать использование CPU, I/O, locks, buffer cache, размера очередей и задержки репликации или CDC.
Отдельный прогон выполняется на холодном кэше. Прогрев даёт вторую картину, полезную для понимания стоимости подключения узла и восстановления после перезапуска. Отказоустойчивость проверяется вместе с нагрузкой: репликация, переключение узла, разрыв соединения с внешним сервисом и задержка CDC.
Индексы, схема и конфигурация
Оптимизация начинается с модели данных и плана запроса. EXPLAIN ANALYZE показывает фактические сканы, joins, сортировки и число обращений к строкам. Сверка плана с ожиданиями часто обнаруживает отсутствующий индекс, неселективный предикат или неудачный порядок колонок в составном индексе.
Составной индекс проектируется под конкретный шаблон доступа: фильтрация, соединение и сортировка должны использовать его совместно. Индексы ускоряют чтение, но увеличивают затраты на запись, расход памяти и стоимость обновлений. Лишние индексы редко видны в описании архитектуры, хотя напрямую снижают TPS.
Партиционирование помогает с pruning, управлением жизненным циклом и параллельной обработкой внутри одной системы. Оно не создаёт независимые узлы, автоматические транзакционные гарантии или отказоустойчивость распределённого кластера.
Дефолтные параметры СУБД рассчитаны на общий класс нагрузки. Размер памяти, параметры журналирования, checkpoints, connection pool и поведение планировщика требуют настройки под фактический профиль. Переполненный пул соединений выглядит как деградация самой базы, хотя запросы ещё не достигли её CPU и диска.
Масштабирование: последовательность важнее максимальной ёмкости
Вертикальное расширение, репликация, кэширование, партиционирование и шардинг решают разные задачи. Их стоимость несопоставима, а побочные эффекты не всегда видны на этапе внедрения.
| Механизм | Какую нагрузку снимает | Какое ограничение сохраняется | Новый риск |
|---|---|---|---|
| Вертикальное расширение | CPU, память, I/O одного узла | Физический предел узла | Стоимость, единая точка отказа |
| Репликация чтения | Часть запросов на чтение | Узкое место записи, lag реплики | Устаревшие данные, сложная маршрутизация |
| Кэширование | Повторные чтения горячих данных | Согласованность и инвалидация | Отдельный компонент, cache stampede |
| Партиционирование | Pruning, обслуживание, локальная параллельность | Глобальные операции и распределённый SLA | Ошибки проектирования ключа |
| Шардинг | Распределение данных и операций между узлами | Транзакции, уникальность и join между частями | Горячие шарды, resharding, сложный recovery |
| Отдельное аналитическое хранилище | Изоляция OLAP от транзакционного пути | Свежесть копии | CDC, reconciliation, две системы сопровождения |
Репликация чтения распределяет только те запросы, которые допускают чтение с задержкой и могут быть направлены на реплику. Она не устраняет конкуренцию за запись в основной базе.
Шардинг рационален после использования резервов вертикального расширения, корректных индексов, репликации чтения и подходящего партиционирования. До этого он создаёт межсерверные транзакции, распределённые соединения, глобальные ограничения уникальности и операции обслуживания нескольких наборов данных.
Шардинг распределяет уже измеренную нагрузку. На старте проекта он добавляет координацию к проблеме, размер которой ещё не определён.
Ключевые решения при шардинге фиксируются заранее:
- shard key определяет распределение трафика и риск hotspot;
- глобальная уникальность требует отдельного протокола;
- транзакция между шардами меняет модель отказов;
- join, range scan и агрегация могут требовать пересылки данных;
- resharding должен иметь сценарий и ограничение по времени;
- backup, restore и disaster recovery рассчитываются для всей системы;
- RPO и RTO включают восстановление topology, а не только запуск одного узла.
Рабочий алгоритм выбора архитектуры
1. Зафиксировать профиль нагрузки. Описать операции, данные, конкуренцию, p95, p99, RPO и RTO.
2. Разделить execution path. Аналитические запросы вынести из транзакционного пути, если они нарушают бюджет задержки.
3. Определить модель согласованности. Сопоставить каждый бизнес-инвариант с ACID, snapshot или eventual consistency.
4. Выбрать топологию. Зафиксировать роль реплик, зон отказа, маршрутизацию запросов и допустимость stale reads.
5. Построить путь данных. Определить формат CDC, идемпотентность, обработку schema change, backfill и сверку.
6. Провести нагрузочный и отказный тест. Использовать репрезентативные данные и измерить хвост задержки вместе с recovery.
7. Задокументировать пределы. Указать, какое решение перестанет масштабироваться и по какому признаку запускается следующий этап.
Решение готово к эксплуатации, если в документации есть ответы на шесть вопросов:
- Какой профиль нагрузки измерен на последнем прогоне?
- Какое значение
p99считается допустимым для каждого класса операций? - Какие данные могут быть потеряны при аварии за RPO?
- Какие гарантии согласованности даёт каждая бизнес-операция?
- Как обнаруживается и устраняется расхождение между транзакционной базой и аналитической витриной?
- Почему выбранная схема масштабирования лучше следующей по стоимости и эксплуатационной сложности?
Если ответов нет, выбор остаётся сравнением маркетинговых описаний. Архитектура начинается с измеримого профиля, явной модели согласованности и ограничений отказа. Продукт подбирается под эти требования.