Как устроена база данных для микросервисов
Что проверяют
Понимаете ли вы, почему у каждого сервиса свои данные и чем за это приходится платить. Следом почти всегда идёт вопрос: как провести операцию через два сервиса, если общей транзакции нет.
Как отвечать
Основной принцип: database per service. Сервис единственный владелец своих таблиц, остальные получают данные только через его API или события. Физически это отдельный кластер, отдельная база или отдельная схема со своей учёткой. Главное, что чужие таблицы напрямую никто не читает.
Дальше последствия и инструменты:
- транзакции:
@Transactionalработает только внутри одного сервиса, двухфазный коммит между сервисами почти не используют. Вместо него сага: цепочка локальных транзакций с компенсациями, через оркестратор (Camunda, Temporal) или хореографию событий; - outbox: запись в бизнес-таблицу и в таблицу
outboxидёт одной локальной транзакцией, а отдельный процесс или Debezium через CDC переносит события в Kafka. Иначе рано или поздно случится «записал в базу, но упал до отправки»; - чтение из нескольких сервисов: API composition или CQRS с отдельной моделью для чтения, собранной из событий;
- согласованность: eventual consistency, и бизнесу нужно явно сказать, где задержка допустима;
- идемпотентность: сообщение доставляется минимум один раз, консьюмер хранит ключи обработанных событий;
- хранилище: PostgreSQL для большинства сервисов, Redis для кэша, OpenSearch для поиска. Разные базы оправдывает нагрузка, а не мода.
Миграции схемы каждый сервис ведёт сам через Flyway или Liquibase.
Где спотыкаются
Оставляют общую базу и называют это микросервисами. Любое изменение таблицы ломает соседей, релизы снова связаны.
Путают Event Sourcing с сагой. Event Sourcing это способ хранить состояние журналом событий, распределённую транзакцию он сам не решает и заметно усложняет систему.
Не продумывают компенсацию. Сага без обратных шагов и без обработки повторов оставляет заказ оплаченным, но без резерва товара.