Представьте пятничный вечер, релиз крупного финтех-модуля и внезапное сообщение в корпоративном чате: диск на мастере PostgreSQL заполнен на 98%, а аналитика отстает от продакшена на четыре часа (классическое «но оно же на моем ноутбуке работало»). Когда микросервисы начинают отчаянно «стучаться» в базу за каждым изменением, классические подходы сдаются первыми. В современной распределенной архитектуре синхронизация данных — это минное поле, где один неверный запрос может положить всю систему. Именно поэтому мы прошли путь от ленивого поллинга до отказоустойчивого CDC, научив PostgreSQL отдавать 5000 RPS без единой слезинки со стороны основного приложения. Давайте разберем этот маршрут без прикрас.
Введение: Зачем нам понадобился Change Data Capture
В современной микросервисной архитектуре монолитные базы данных постепенно уходят в прошлое, уступая место распределенным системам. Однако PostgreSQL по-прежнему остается надежным фундаментом для хранения транзакционных данных. Рано или поздно перед любой растущей IT-компанией встает классическая задача: как синхронизировать состояние БД с внешними сервисами, поисковыми движками, аналитическими хранилищами и кешами без создания избыточной нагрузки на саму СУБД?
Долгое время стандартным ответом были триггеры или регулярный поллинг таблиц по полю updated_at. Но давайте будем честны: триггеры снижают производительность записи и съедают ресурсы в пиковые нагрузки, а поллинг порождает лаг репликации и создает огромный шум в логах (заставляя сисадминов грустно смотреть на графики в Grafana). Именно поэтому мы внедрили CDC (Change Data Capture) на базе PostgreSQL. В этой статье мы подробно расскажем об архитектурных вызовах, выборе инструментов и построении отказоустойчивого конвейера данных, который обрабатывает более 5000 RPS без деградации основного приложения.
Когда бизнес требует мгновенной реакции систем на любые изменения пользователей, на сцену выходит логическая репликация — и вот как мы подошли к ее выбору.
1. Выбор архитектуры: Логическая репликация против триггеров и поллинга
На старте проекта перед архитекторами стоял выбор из трех классических подходов:
- Поллинг по таймеру: создает высокую нагрузку на диск (sequential scans), пропускает быстрые обновления и не умеет фиксировать hard deletes без использования soft delete.
- Триггеры уровня строк: увеличивают latency транзакций, раздувают таблицы за счет очередей и несут риск падения приложения при переполнении очередей.
- Логическая репликация (Logical Decoding): считывает изменения напрямую из Write-Ahead Log (WAL).
Мы остановились на логической репликации. Начиная с PostgreSQL 10+, механизм Logical Decoding позволяет транслировать бинарный поток WAL в структурированные события (insert, update, delete). Это полностью изолирует процесс сбора данных от транзакционного потока — никаких накладных расходов на запись в таблицы.
Определившись с источником правды, оставалось выстроить надежный мост между бинарными логами и нашими микросервисами.
2. Архитектура конвейера CDC: От WAL до потребителей
Наша итоговая система состоит из трех ключевых компонентов: источника (PostgreSQL), транспортного слоя (Debezium + Kafka) и потребителей (микросервисы на Go и Java).
[ PostgreSQL WAL ] ---> [ Debezium Connector ] ---> [ Apache Kafka ] ---> [ Microservices / ClickHouse ]
В роли коннектора мы выбрали Debezium, так как он берет на себя управление смещениями (offsets), сериализацию данных и обработку схемы (schema evolution). События упаковываются в JSON/Avro и отправляются в топики Apache Kafka.
Однако теория никогда не совпадает с реальностью продакшена на 100% (и Stack Overflow почему-то не содержит готового ответа на наш уникальный баг). Переложив эту схему на боевые рельсы, мы быстро познакомились с местными «сюрпризами».
3. С какими граблями мы столкнулись в продакшене
Теория выглядит красиво, но реальн