Почему одной базы данных иногда недостаточно
Эволюция архитектуры: от одной базы к экосистеме хранения
На старте проекта архитектура данных обычно выглядит просто: есть приложение, есть база данных, приложение читает из неё информацию и записывает обратно.
Приложение ──► СУБД (PostgreSQL)
Для многих систем этого действительно достаточно. Например, интернет-магазину может хватить PostgreSQL, чтобы хранить товары, клиентов, заказы, оплаты и остатки. Пока объём данных умеренный, запросы понятны, а нагрузка предсказуема, отдельные хранилища только усложнили бы систему.
Объём умеренный + Запросы понятны + Нагрузка предсказуема.
Но затем приложение начинает расти. Появляются новые вызовы:
- › аналитика по нескольким годам истории;
- › пользователям нужны быстрые фильтры и поиск;
- › в систему загружаются документы и изображения;
- › возникает кэширование;
- › появляются события и журналы;
- › после внедрения ИИ возникает задача искать не только точное совпадение текста, но и документы по смыслу.
И постепенно оказывается, что вопрос уже звучит не так:
А так:
Именно в этот момент одной СУБД может стать недостаточно.
Одна база — не обязательно плохая архитектура
Сразу стоит убрать распространённое заблуждение: использование нескольких хранилищ само по себе не делает систему более зрелой. Наоборот, если можно решить задачу одной PostgreSQL, это часто хороший вариант.
Одна база данных означает:
- › меньше инфраструктуры;
- › меньше компонентов, которые могут выйти из строя;
- › проще резервное копирование;
- › проще мониторинг;
- › проще разработка;
- › меньше механизмов синхронизации;
- › меньше требований к компетенциям команды.
Поэтому архитектура не должна начинаться с идеи: «Нам нужны PostgreSQL, Redis, Elasticsearch, S3 и векторная база».
Она должна начинаться с другого вопроса:
Если все сценарии нормально обслуживаются одной системой, добавлять остальные компоненты незачем. Проблема появляется тогда, когда разные сценарии начинают конфликтовать между собой.
У разных данных — разные требования
Представим промышленную информационную систему. Она хранит разнородную информацию:
- › оборудование;
- › сотрудников;
- › производственные задания;
- › параметры технологических операций;
- › результаты измерений;
- › документы;
- › фотографии;
- › историю событий;
- › показатели производства.
Формально всё это можно попытаться положить в одну PostgreSQL. И на первом этапе это может работать. Но теперь рассмотрим, что происходит при реальной эксплуатации.
Возникает конфликт двух совершенно разных моделей нагрузки:
Операционная система должна быстро получить:
└─► Производственная линия
└─► Текущий статус
└─► Последнее измерение
Требует быстрых транзакционных операций.
А аналитическая система в это же время хочет выполнить другой запрос:
└─► Фильтрация по цехам
└─► Группировка по оборудованию
└─► Расчёт трендов (миллионы записей)
Требует чтения и обработки огромных объёмов данных.
Для пользователя это две разные задачи. Для базы данных — две совершенно разные модели нагрузки. И если заставить одну систему одинаково хорошо решать обе задачи, рано или поздно приходится идти на компромиссы.
Операционная база: система должна быстро работать здесь и сейчас
Классическая реляционная СУБД хорошо подходит для операционных данных.
Например, PostgreSQL может хранить:
└── Заказ
└── Позиции заказа
└── Оплата
└── Отгрузка
Здесь важны:
- › транзакции;
- › целостность данных;
- › связи между сущностями;
- › ограничения;
- › предсказуемость операций;
- › корректное обновление состояния.
Если пользователь нажимает «Создать заказ», systemа должна не просто записать несколько строк. Она должна гарантировать, что операция либо выполнена корректно, либо не выполнена вообще. Это классическая зона ответственности транзакционной базы данных.
Но операционная база не обязана быть лучшим местом для всего остального.
Когда аналитика начинает мешать операционной системе
Одна из наиболее распространённых причин разделения хранилищ — аналитическая нагрузка.
Допустим, ERP-система содержит несколько десятков миллионов операций. Бизнес хочет получить отчёт:
Запрос можно выполнить непосредственно в операционной базе. Но если таких запросов становится много, возникает проблема: аналитика начинает конкурировать с рабочими операциями.
Причём проблема не обязательно появляется из-за плохих SQL-запросов. Даже хорошо написанный аналитический запрос может быть тяжёлым просто потому, что ему приходится читать большой объём данных.
Поэтому в архитектуре часто появляется отдельный аналитический контур:
Теперь рабочая база отвечает за операционную деятельность, а аналитическая система — за интенсивное чтение и агрегацию истории. Это не означает, что PostgreSQL «не умеет аналитику». Он умеет. Вопрос в масштабе и характере нагрузки.
Кэш — это тоже отдельная задача
Другая ситуация возникает, когда данные нужно получать очень часто, но вычислять или читать из основной базы каждый раз дорого.
Например, главная страница интернет-магазина постоянно запрашивает:
- › популярные товары;
- › настройки;
- › текущие акции;
- › часто используемые справочники;
- › результаты тяжёлых вычислений.
Если каждый запрос идёт непосредственно в PostgreSQL, база получает огромное количество повторяющихся операций. В этом случае можно использовать кэш.
Алгоритм работы распределенного кэширования:
Кэш принципиально отличается от основной базы. Его задача — не быть окончательным источником истины. Если Redis очистился, система должна иметь возможность восстановить данные из основного источника. Это важное архитектурное различие.
Кэш ускоряет систему. Он не должен определять её состояние.
Файлы — не всегда данные для реляционной базы
Ещё одна типичная ошибка — хранить большие файлы непосредственно внутри операционной базы без необходимости.
Представим систему документооборота. В ней есть метаданные:
- › Документ
- › Автор
- › Дата
- › Тип
- › Статус
и сам PDF-файл размером 20 МБ. Метаданные документа вполне логично хранить в PostgreSQL:
author_id
created_at
document_type
status
file_path
А сам файл — в объектном хранилище. Например:
Почему? Потому что задача реляционной базы здесь — управлять сущностью документа и её связями. Задача объектного хранилища — надёжно хранить большой бинарный объект. Это разные типы нагрузки.
Поиск тоже может потребовать отдельного инструмента
Реляционная база прекрасно умеет искать данные. Но поиск бывает разным.
Есть запрос:
Достаточно точного поиска по структурированному полю.
И есть другой запрос:
Требуется полнотекстовый поиск, индексация текста или семантический поиск.
Поэтому в больших системах может появиться отдельный поисковый движок. При этом исходные данные не обязательно переносятся из основной базы. Часто создаётся производная поисковая модель:
Здесь важно понимать принцип: поисковый индекс может быть производным представлением данных, а не их главным источником. Если индекс потерян, его можно построить заново из исходных данных.
Векторные базы появились по другой причине
Системы с ИИ добавили ещё один тип поиска. Важно понимать разницу в подходах:
Обычная база отвечает на вопрос:
Полнотекстовый поиск может отвечать:
Векторный поиск отвечает на другой вопрос:
Документ в базе: «При перегреве подшипников необходимо остановить оборудование и проверить систему смазки».
Запрос пользователя: «Что делать, если узел начинает сильно нагреваться?»
Точного совпадения слов может не быть. Но смысл запроса близок к содержанию документа.
Для такого сценария текст превращается в векторное представление:
Запрос пользователя проходит похожий путь:
Это уже совершенно другой тип индексации.
Но векторная база не означает, что PostgreSQL больше не нужен
Это принципиальный момент. Иногда достаточно расширения PostgreSQL для работы с векторами.
То есть архитектура может выглядеть так:
Отдельная специализированная векторная база появляется тогда, когда требования к поиску, масштабу, нагрузке или архитектуре делают отдельное решение оправданным.
То есть вопрос снова не:
А:
Самая сложная часть — синхронизация
Как только в системе появляется несколько хранилищ, возникает новая проблема: данные теперь нужно согласованно доставлять в несколько мест.
Пока данные только записываются в PostgreSQL, всё относительно просто. Но теперь возникает вопрос: Что произойдёт, если запись в PostgreSQL прошла успешно, а обновление поискового индекса — нет? Ответ зависит от архитектуры.
В простом варианте приложение может попытаться обновить оба хранилища. Но такой подход быстро приводит к сложным сценариям отказа.
Поэтому часто используется событийная модель взаимодействия через специализированный брокер.
Теперь системы получают событие:
- › ProductUpdated
- › OrderCreated
- › DocumentUploaded
- › MachineStatusChanged
и самостоятельно обновляют свои представления. Это позволяет отделить источник истины от производных представлений.
Источник истины должен быть определён заранее
При нескольких хранилищах особенно опасно состояние десинхронизации, когда в разных базах находятся разные данные и непонятно, чему верить. Поэтому для каждой сущности необходимо понимать, где находится authoritative source — источник истины.
Например, распределение ролей для сущности «Заказ»:
Это очень простая схема, но она предотвращает большое количество архитектурных проблем. Если поисковый индекс отличается от PostgreSQL, это не обязательно означает потерю данных. Возможно, индекс просто ещё не обновился.
Это называется eventual consistency — согласованность в конечном счёте.
Для некоторых сценариев это нормально.
Для других — совершенно недопустимо.
Нельзя бездумно переносить данные между системами
Команда замечает, что появилась аналитическая нагрузка, и решает применить ситуативные решения:
«Давайте просто скопируем всю базу в DWH».
«Давайте отправим всё в Elasticsearch».
«Давайте положим все данные в Kafka».
Но наличие инструмента ещё не означает наличие архитектуры.
Перед переносом нужно понять:
- › Какие данные действительно нужны?
- › Для какого сценария?
- › С какой задержкой они должны появляться?
- › Как часто обновляются?
- › Кто является источником истины?
- › Как обрабатываются удаления?
- › Что происходит при повторной доставке события?
- › Что произойдёт при недоступности целевого хранилища?
- › Как восстанавливается индекс?
- › Как контролируется расхождение данных?
Последний пункт особенно важен. Когда система состоит из одного хранилища, согласованность в основном контролирует сама СУБД.
Когда хранилищ становится несколько, часть согласованности становится обязанностью архитектуры приложения и интеграционного слоя.
Несколько хранилищ — это цена, а не бесплатное ускорение
У полиглота из баз данных есть обратная сторона.
Каждый новый компонент добавляет:
- - мониторинг;
- - резервное копирование;
- - обновления;
- - контроль доступа;
- - диагностику;
- - документацию;
- - отказоустойчивость;
- - требования к специалистам;
- - интеграционные механизмы.
Поэтому архитектура:
+ Redis
+ Kafka
+ Elasticsearch
+ ClickHouse
+ S3
+ Vector DB
не является автоматически более профессиональной...
...чем одна базовая реляционная СУБД.
Иногда первая архитектура необходима. А иногда она просто говорит о том, что команда добавляла технологии по мере появления модных терминов.
Как понять, что пора разделять хранилища
Есть несколько практических сигналов.
Аналитика начинает влиять на работу приложения
Если тяжёлые аналитические запросы конкурируют с транзакционными операциями, стоит разделить нагрузки.
Один тип данных имеет принципиально другую природу
Большие бинарные объекты, например видео, документы и изображения, не обязательно должны храниться там же, где транзакционные сущности.
Требования к скорости чтения отличаются
Если одни данные должны обслуживаться за миллисекунды из памяти, а другие могут обрабатываться пакетно, разные механизмы хранения могут быть оправданы.
Появляется специализированный поиск
Если системе нужен полнотекстовый или семантический поиск по большим объёмам данных, специализированный индекс может оказаться эффективнее обычных SQL-запросов.
5. Разные данные имеют разный жизненный цикл
Например:
Необязательно хранить весь массив истории в том же формате и на том же уровне доступности, что и оперативные данные.
Масштабирование одной части системы стало невозможным
Если поиск требует в десять раз больше ресурсов, чем транзакции, масштабировать всю базу ради поиска нерационально.
А когда разделять не стоит
Есть и обратная сторона. Если проект небольшой, требования понятны, объёмы умеренные, а PostgreSQL закрывает все задачи — лучше оставить PostgreSQL.
- - Не стоит добавлять Redis только потому, что он используется в больших проектах.
- - Не стоит добавлять Kafka, если система не требует событийного обмена.
- - Не стоит внедрять отдельный Elasticsearch, если PostgreSQL нормально решает поиск.
- - Не стоит создавать векторную базу, если семантического поиска в системе вообще нет.
И особенно не стоит строить распределённую архитектуру ради ощущения «серьёзной системы».
Хорошая архитектура — не та, в которой больше компонентов.
Хорошая архитектура — та, в которой каждый компонент решает конкретную проблему.
Архитектура должна следовать сценариям
Удобно смотреть на систему не через список технологий, а через потоки данных.
Здесь нет задачи сделать все системы независимыми. Наоборот. Каждая отвечает за тот сценарий, который ей подходит лучше всего.
- › PostgreSQL отвечает за операционные данные.
- › Redis — за быстрый доступ к часто используемым данным.
- › Object Storage — за файлы.
- › Аналитическое хранилище — за исторический анализ.
- › Поисковый индекс — за специализированный поиск.
- › Векторный индекс — за семантический поиск.
- › Message Broker — за доставку событий между компонентами.
Самая важная мысль
Вопрос «какую базу данных выбрать?» часто задаётся слишком рано. Сначала нужно понять: что система делает с данными.
Характер обработки информации принципиально отличается:
- › Одни данные создаются и изменяются в рамках транзакций.
- › Другие читаются миллионами строк для аналитики.
- › Третьи должны мгновенно отдаваться из памяти.
- › Четвёртые являются большими файлами.
- › Пятые используются для полнотекстового поиска.
- › Шестые — для поиска по смыслу.
И совершенно нормально, что для таких разных задач существуют разные инструменты.
Но это не означает, что каждой системе обязательно нужны все перечисленные технологии.
Иногда зрелая архитектура выглядит так:
А иногда — так:
Обе архитектуры могут быть правильными. Разница определяется не количеством технологий, а требованиями системы.
Вместо универсальной базы — подход под задачу
Современная информационная система редко является просто «приложением и базой данных». По мере роста она становится набором разных контуров обработки информации.
Но переход от одной СУБД к нескольким должен происходить не потому, что «так делают большие компании».
Он должен происходить тогда, когда появляется конкретная архитектурная причина:
- › разные профили нагрузки;
- › разные требования к задержке;
- › разные модели запросов;
- › разные объёмы;
- › разные типы данных;
- › разные требования к хранению;
- › специализированный поиск;
- › аналитика;
- › ИИ-сценарии.
В конечном счёте хороший выбор хранилища — это не выбор самой популярной технологии.
Это соответствие способа хранения тому, как система реально использует информацию.
И чем сложнее становится система, тем важнее сначала разобраться в потоках данных, границах ответственности и требованиях к каждому сценарию — и только после этого решать, сколько баз данных ей действительно нужно.
API, Webhook или очередь сообщений?
Когда API есть, а интеграция всё равно не работает
Как соединить производство и корпоративные системы
Все инженерные материалы и экспертные статьи
Полный алфавитный справочник ИТ-терминов
🔒 Данные и доступ к системам заказчика обрабатываются в соответствии с согласованными требованиями к безопасности и конфиденциальности. Условия доступа, хранения и обработки данных определяются архитектурой проекта и договором.
Сайт носит исключительно информационный характер и не является публичной офертой в соответствии со статьёй 437 Гражданского кодекса РФ. Цены, состав и условия предоставления услуг уточняются при проектировании решения и фиксируются в договоре. Условия технического сопровождения и уровень сервиса могут дополнительно определяться отдельным соглашением (SLA).
Автоматизация бизнеса и ИИ-решения под ключ
Самозанятый Скопец Антон Викторович ИНН 741709260870