CDC (Change Data Capture) в связке PostgreSQL → Debezium → Kafka/Redpanda — это надёжный способ проталкивать изменения из OLTP-базы в стриминговую шину для аналитики, событийной интеграции и реактивных микросервисов. Но за простым обещанием «включить логическое декодирование и поставить коннектор» скрывается масса нюансов: настройки logical decoding, контроль replication slots, выбор плагина (pgoutput или wal2json), компоновка топиков, snapshot-стратегия, безопасность и мониторинг отставания.
Зачем CDC в PostgreSQL и когда он уместен
CDC позволяет получать поток событий о вставках, обновлениях и удалениях почти в реальном времени, не трогая приложение и не создавая тяжёлые триггеры. Это снижает связность систем: исходная база не знает о потребителях событий, а downstream‑компоненты (аналитика, кэширование, индексы поиска, интеграции) получают свежие данные из единого логического потока.
Основные сценарии:
- Актуализация витрин/ODS/ELT: кластеры ClickHouse/Spark/OLAP берут изменения из Kafka/Redpanda.
- Событийная интеграция микросервисов: публикуем доменные события и реагируем на них асинхронно.
- Кеширование и денормализация: оперативная синхронизация материалов для поиска, рекомендаций, API-слоёв.
CDC — это не SQL‑репликация и не бэкапы. Это поток изменений на уровне журналов WAL, представленный в удобном формате для consumers.
Основы logical decoding в PostgreSQL
Логическое декодирование разбирает WAL‑записи в понятные для клиента события. Для этого включается wal_level=logical, а потребители подключаются как реплика и читают изменения через слот (replication slot). Слот фиксирует прогресс чтения: пока потребитель не подтвердит, что обработал LSN, сервер не удаляет соответствующие WAL‑сегменты. Это и благо (надёжность), и риск (рост диска при лаге).
Ключевые элементы:
- wal_level=logical — разрешает формирование логического потока.
- Replication slots — удерживают WAL до подтверждённой позиции (
confirmed_flush_lsnдля логических слотов). - Output plugin — преобразует события:
pgoutput(встроенный, используется логической репликацией и Debezium по умолчанию) илиwal2json(формат JSON).
pgoutput vs wal2json
pgoutput — стандартный, поддерживает DDL‑события и хорошо интегрирован с публикациями/подписками. Debezium 2.x использует pgoutput по умолчанию и умеет автоматически создавать PUBLICATION под заданные таблицы (или создавайте публикации вручную под нужные права).
wal2json даёт чистый JSON и популярен там, где он уже принят как стандарт. Его нужно установить и следить за совместимостью версий. В новых инсталляциях чаще берут pgoutput ради поддержки DDL и отсутствия внешних зависимостей.
Подготовка PostgreSQL к CDC
Минимальные настройки в postgresql.conf (значения подбирайте по своему трафику и SLA):
# postgresql.conf
wal_level = logical
max_replication_slots = 10
max_wal_senders = 10
wal_keep_size = 2048MB
max_worker_processes = 16
max_logical_replication_workers = 8
max_sync_workers_per_subscription = 4
logical_decoding_work_mem = 128MB
shared_buffers = 25% # ориентир, зависит от памяти
Создаём роль для Debezium с минимально необходимыми правами:
CREATE ROLE debezium WITH LOGIN REPLICATION PASSWORD 'strong-password';
GRANT CONNECT ON DATABASE appdb TO debezium;
GRANT USAGE ON SCHEMA public TO debezium;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO debezium;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO debezium;
Для pgoutput расширение не нужно. Для wal2json потребуется установить плагин и, при необходимости, расширение:
-- Если плагин установлен на уровне сервера, расширение может не требоваться
CREATE EXTENSION IF NOT EXISTS wal2json;
Планирование ресурсов: учитывайте дополнительную нагрузку на WAL, сетевой трафик и возможный рост задержек при всплесках. Продумайте мониторинг свободного места под WAL: если consumer отстаёт, слот удержит файлы, и диск начнёт расти. Пороговые алерты по отставанию слотов обязательны. Для соединений репликации не используйте PgBouncer в режиме transaction — только прямое подключение или пулер в режиме session (подробнее см. гайд по PgBouncer и режимам пуллинга).
Debezium: варианты развертывания
Debezium — это набор коннекторов для Kafka Connect, а также самостоятельный Debezium Server. Для Kafka/Redpanda наиболее распространён вариант с Kafka Connect: запускается кластер Connect, туда через REST создаются коннекторы. Redpanda совместим с Kafka API и имеет собственные инструменты управления, но логика та же.
Когда выбирать какой режим:
- Kafka Connect — если у вас классическая Kafka‑инфраструктура, много коннекторов и важно централизованное управление.
- Redpanda + Connect — удобен для компактных установок без ZooKeeper, быстрый старт, те же API.
- Debezium Server — если Kafka Connect избыточен и нужно отправлять в разные целевые системы без отдельного Connect‑кластера. Для Kafka/Redpanda чаще остаются на Connect.
Практический совет: под коннекторы, брокеры и ZooKeeper/контроллеры удобно выделять изолированные инстансы. Для гибкого масштабирования подойдёт облачный VDS для брокеров и Connect, чтобы независимо наращивать CPU/SSD и сеть.
Минимальная конфигурация коннектора Debezium PostgreSQL
Пример конфигурации для Kafka Connect, использующей pgoutput и автосоздание публикации только для выбранных таблиц:
{
"name": "pg-app-01",
"config": {
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
"database.hostname": "db.internal",
"database.port": "5432",
"database.user": "debezium",
"database.password": "******",
"database.dbname": "appdb",
"topic.prefix": "app",
"plugin.name": "pgoutput",
"slot.name": "debezium_app",
"slot.drop.on.stop": "false",
"publication.name": "app_pub",
"publication.autocreate.mode": "filtered",
"schema.include.list": "public",
"table.include.list": "public.orders,public.order_items",
"column.exclude.list": "public.users.password_hash",
"snapshot.mode": "initial",
"snapshot.fetch.size": "2048",
"include.schema.changes": "true",
"provide.transaction.metadata": "true",
"tombstones.on.delete": "false",
"heartbeat.interval.ms": "5000",
"max.batch.size": "2048",
"max.queue.size": "8192",
"decimal.handling.mode": "string",
"time.precision.mode": "connect",
"database.sslmode": "require"
}
}
Комментарии к параметрам:
topic.prefixформирует имена топиков: по умолчаниюapp.public.ordersи т.п.publication.autocreate.mode=filtered— Debezium создастPUBLICATIONтолько для выбранных таблиц (либо создайте публикацию вручную и выдайте права на неё).slot.drop.on.stop=false— слот сохраняется между рестартами, что безопаснее для RPO, но требует мониторинга лага.snapshot.mode:initial— единоразовый снимок и затем поток;initial_only— только снимок;never— сразу WAL без начального дампа (нужна синхронизация запуска).include.schema.changes=true— в отдельный топик пойдут DDL‑события, полезно для эволюции схемы.
Выбор и настройка формата сообщений
Debezium может публиковать в Avro, JSON, Protobuf. На практике JSON — быстрый старт, Avro — строгая схема и лучшая компрессия. Для JSON-потока часто отключают схемы (value.converter.schemas.enable=false) и получают компактные полезные нагрузки.
Ключ сообщений строится на основе первичного ключа таблицы. Это важно для партиционирования и компакции. Если в таблице нет PK, укажите REPLICA IDENTITY FULL или добавьте суррогатный ключ, иначе UPDATE/DELETE не будут однозначно сопоставимы.
ALTER TABLE public.orders REPLICA IDENTITY FULL;

Паттерн Outbox: доменные события без «шторма» DDL
Outbox — способ формировать стабильный, обратно совместимый поток доменных событий, не раскрывая всю схему БД. Приложение пишет бизнес‑события в таблицу outbox, CDC считывает только её и маршрутизирует в топики по типу события. Это уменьшает связанность и облегчает эволюцию схем.
Пример таблицы:
CREATE TABLE public.outbox_events (
id uuid primary key,
aggregate_id uuid not null,
aggregate_type text not null,
event_type text not null,
payload jsonb not null,
headers jsonb,
created_at timestamptz not null default now()
);
И фрагмент конфигурации коннектора с SMT Debezium Outbox:
{
"config": {
"table.include.list": "public.outbox_events",
"transforms": "outbox",
"transforms.outbox.type": "io.debezium.transforms.outbox.EventRouter",
"transforms.outbox.table.expand.json.payload": "true",
"transforms.outbox.route.by.field": "aggregate_type",
"transforms.outbox.route.topic.replacement": "app.outbox.${routedByValue}",
"key.converter": "org.apache.kafka.connect.storage.StringConverter",
"value.converter": "org.apache.kafka.connect.json.JsonConverter",
"value.converter.schemas.enable": "false"
}
}
В результате события типа, например, order пойдут в топик app.outbox.order, а полезная нагрузка будет «распакована» из поля payload.
Kafka или Redpanda: что учесть
Обе системы совместимы по API. Redpanda проще в развёртывании и не требует ZooKeeper, что удобно для компактных установок и тестовых контуров. Kafka проверена в больших кластерах и богата экосистемой. Ключевые моменты для CDC:
- Топики: для таблиц с большим числом ключей и интенсивными изменениями настройте достаточное число партиций.
- cleanup.policy: для changelog‑потоков часто ставят
compact(оставляет последние версии по ключу). Для событийного outbox —deleteс нужнойretention.ms. - acks и durability: выбирайте уровень подтверждения продюсера в зависимости от SLA и синхронности хранилища.

Производительность и надёжность
На стороне PostgreSQL обратите внимание на параметры, влияющие на поток изменений:
logical_decoding_work_mem— объём памяти на пакет декодирования. Если видите частые spill’ы, увеличьте.wal_compression— может помочь снизить I/O при активных UPDATE, но увеличит CPU.full_page_writes— безопасность прежде всего; отключайте только осознанно и обычно не в проде.- Размер
wal_keep_sizeкак страховка от мгновенного удаления WAL при кратковременных лагах.
На стороне Debezium/Kafka Connect:
max.batch.sizeиmax.queue.size— увеличивайте для throughput, следите за задержками.poll.interval.ms— частота опроса БД; слишком малая — лишняя нагрузка, слишком большая — рост задержек.- Используйте мониторинг метрик Connect/коннектора и алерты на рост очереди, ошибки конвертации, переработки снапшота.
Семантика доставки: Debezium обеспечивает «по крайней мере один раз» на уровне коннектора. Для строгой дедупликации проектируйте идемпотентных консьюмеров и используйте ключи сообщений, позволяющие безопасно переигрывать поток. Для планирования WAL‑ретенции и аварийного восстановления пригодится подробный разбор PITR и архивирования WAL.
Мониторинг и алертинг
Главный риск — отставание слотов и рост диска под WAL. Наблюдайте pg_replication_slots и LSN‑лаг:
SELECT slot_name,
active,
plugin,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS lag
FROM pg_replication_slots
WHERE database = current_database();
Также полезно контролировать публикации и покрытие таблиц:
SELECT pubname, puballtables, pubinsert, pubupdate, pubdelete FROM pg_publication;
SELECT * FROM pg_publication_tables WHERE pubname = 'app_pub';
Повесьте алерты на:
- Лаг слота по байтам и по времени (оценочно по скорости поступления WAL).
- Свободное место в разделе с WAL и каталоге данных.
- Ошибки конвертеров в Connect (например, несериализуемые типы, большие строки).
Безопасность
Подключение Debezium к PostgreSQL шифруйте и ограничивайте:
- Включайте SCRAM‑аутентификацию и TLS для клиентских подключений.
- Минимальные привилегии: доступ только к нужной БД/схемам/таблицам, роль
REPLICATION, запрет на DDL. - Сетевые ACL/фаервол: брокеры и Connect не должны быть доступны из внешних сетей без необходимости.
Пример фрагмента конфигурации коннектора для TLS в БД (значения и пути условны):
{
"config": {
"database.sslmode": "verify-full",
"database.sslrootcert": "/etc/ssl/ca.pem",
"database.sslcert": "/etc/ssl/client.crt",
"database.sslkey": "/etc/ssl/client.key"
}
}
Если у вас внешний доступ к брокерам/Connect, для публичных эндпоинтов используйте проверенные SSL-сертификаты, чтобы избежать проблем с клиентами и ротацией самоподписанных цепочек.
Частые ошибки и как их избежать
- Таблица без PK: UPDATE/DELETE неидемпотентны. Решение — добавить PK или
REPLICA IDENTITY FULL(понимая рост нагрузки). - Отставание слота и переполнение диска: включить алерты, ограничить batch‑размеры, масштабировать консьюмеров, временно повысить
wal_keep_size, при кризисе — остановить write‑нагрузку или временно остановить неважные коннекторы. - Снапшот «долго и больно»: используйте
snapshot.fetch.size, исключайте лишние таблицы, запускайте в тихое окно. При больших БД — staged‑подход: сначала ключевые таблицы, затем остальные. - DDL‑шторм: при активной эволюции схемы используйте Outbox, отделяйте доменные события от структуры OLTP.
- Сброс слота: не удаляйте слот без осознания последствий — потеряете непрочитанные изменения. Если нужно пересоздать — зафиксируйте LSN и пересоберите поток.
Схема и типы данных
Debezium транслирует типы PostgreSQL в типы Kafka Connect. Обратите внимание на numeric и временные типы: decimal.handling.mode и time.precision.mode влияют на сериализацию. Для геометрии и пользовательских типов предусмотрите сериализацию в строки/JSON. Если у вас жёсткие схемы downstream (Avro), заранее планируйте совместимость и миграции.
Redpanda на практике
Redpanda удобна для небольших команд: один бинарник, низкая латентность, совместимость с экосистемой Kafka. Проверьте настройки топиков под CDC: для changelog‑потоков — cleanup.policy=compact, для событий — delete и адекватная retention.ms. Следите за размером сегментов и компрессией. Для быстрой диагностики используйте утилиты управления кластерами и экспорт метрик, чтобы видеть задержки продюсера/консьюмера.
Порядок развертывания: проверенный чек‑лист
- Оцените сценарии: нужен ли outbox, требования к ретенции и ключам топиков.
- Подготовьте PostgreSQL:
wal_level=logical, слоты/сендеры, права для роли CDC. - Определите таблицы для публикации, проверьте PK и
REPLICA IDENTITY. - Разверните Kafka/Redpanda и Kafka Connect, спроектируйте топики и политику хранения.
- Создайте коннектор Debezium:
pgoutput(илиwal2json, если это ваш стандарт). - Запустите
snapshot.initialна тестовом контуре, измерьте лаги и пропускную способность. - Включите мониторинг: лаг слотов, место на диске, метрики коннектора и брокеров.
- Протестируйте ошибки: рестарты коннектора, временно недоступное хранилище, повторы сообщений.
- Подготовьте план инцидентов: действия при росте лага, расширении партиций, смене схемы.
Кстати, если вы активно оптимизируете базу под поток изменений, пригодится и гайд по настройке Autovacuum и индексам.
Итоги
Связка PostgreSQL + Debezium + Kafka/Redpanda — зрелый способ внедрить CDC без боли и с хорошей масштабируемостью. Успех определяется дисциплиной: чёткой зоной ответственности таблиц, продуманной стратегией снапшота, мониторингом replication slots и вниманием к форматам данных. В большинстве новых проектов стоит начинать с pgoutput и Outbox‑паттерна, чтобы получить стабильный событийный поток и спокойно эволюционировать схему, не ломая потребителей.


