Real-Time Backend

Change Data Capture

Интернет-магазин обновил цену товара в PostgreSQL. Через сколько миллисекунд об этом должны узнать Elasticsearch (поиск), Redis (кеш), аналитика и партнёрский API? Ответ Shopify - менее 500. Секрет: они читают не БД, а её журнал.

  • Netflix синхронизирует каталог фильмов между PostgreSQL и Elasticsearch через Debezium - изменения метаданных фильма появляются в поиске менее чем через секунду без единого `SELECT`
  • Airbnb строит near-real-time аналитику бронирований через PostgreSQL WAL -> Kafka -> Hive: задержка от OLTP-записи до аналитического дашборда - менее 1 минуты вместо ночного ETL
  • Shopify использует CDC для синхронизации inventory между складской БД и системами управления заказами: каждое изменение остатка немедленно блокирует overselling
  • LinkedIn разработал Databus (2012) - одну из первых CDC-систем на базе MySQL binlog - для синхронизации профилей пользователей между сервисами; идеи Databus легли в основу современного Debezium

Что такое CDC

**Change Data Capture (CDC)** - это подход, при котором система отслеживает каждое изменение в базе данных (INSERT / UPDATE / DELETE) и публикует его как событие в поток. Вместо того чтобы периодически делать `SELECT` и сравнивать снимки, CDC читает внутренний журнал транзакций - источник абсолютной истины, который БД ведёт и так.

Классический polling выглядит так: сервис каждые 5 секунд спрашивает `WHERE updated_at > last_check`. Проблема - удалённые строки исчезают бесследно, а `updated_at` нужно добавлять руками в каждую таблицу. CDC не требует ни того ни другого: он видит DELETE-запись прямо в лог-файле.

Три источника данных для CDC по возрастанию надёжности: **триггеры БД** (медленно, нагружают БД), **временные метки** (не ловят DELETE), **журнал транзакций** (WAL/binlog - промышленный стандарт).

  • **Log-based CDC** читает WAL (PostgreSQL) или binlog (MySQL) - без доп. нагрузки на таблицы
  • **Событие CDC** содержит: тип операции, таблицу, старые и новые значения полей, LSN/позицию в журнале
  • **At-least-once delivery** - стандарт; потребитель должен уметь обрабатывать дублирующиеся события идемпотентно
  • **Initial snapshot** - перед стримингом нужно выгрузить текущее состояние таблицы; Debezium делает это автоматически

Polling с `updated_at` не замечает одну категорию изменений. Какую?

Debezium

**Debezium** - опенсорсный CDC-движок от Red Hat, де-факто стандарт в экосистеме Kafka. Он работает как набор Kafka Connect коннекторов: читает журнал транзакций каждой БД и публикует события в Kafka-топики. Netflix использует Debezium для синхронизации 10+ микросервисов; LinkedIn - для репликации пользовательского профиля между дата-центрами.

Debezium поддерживает PostgreSQL (через logical replication slots), MySQL/MariaDB (binlog), MongoDB (oplog), Oracle (LogMiner), SQL Server (CDC feature). Один коннектор = одна БД-инстанция.

Shopify использует Debezium для CDC своей inventory-базы: каждое изменение остатков на складе немедленно попадает в Kafka и оттуда - в системы управления заказами, аналитику и партнёрские API. Задержка от записи в БД до обработки downstream - менее 500 мс.

  • **Replication slot** - PostgreSQL хранит WAL до тех пор, пока Debezium его не прочитал; при долгом простое слот может заблокировать очистку WAL и переполнить диск
  • **Offset storage** - Debezium сохраняет последний прочитанный LSN в Kafka (топик `connect-offsets`); при рестарте продолжает с этой точки
  • **Schema registry** - события Debezium используют Avro/JSON Schema; Confluent Schema Registry или Apicurio хранят эволюцию схем

Debezium-коннектор был остановлен на 48 часов из-за инцидента. Какой побочный эффект наиболее вероятен в PostgreSQL?

PostgreSQL Logical Replication

**Logical replication** в PostgreSQL работает поверх WAL (Write-Ahead Log) - журнала, в который PG записывает каждое изменение перед применением. Физическая репликация копирует байты страниц напрямую; логическая декодирует WAL в читаемые SQL-операции и отдаёт их подписчику через replication slot.

Плагин **pgoutput** - встроен в PostgreSQL 10+, не требует установки. Альтернатива **wal2json** выдаёт изменения в JSON прямо из PG - удобно для скриптов, но менее производительно.

Airbnb строит свою data-платформу на PostgreSQL logical replication: события о новых бронированиях, изменениях цен и обновлениях листингов через WAL попадают в Kafka, откуда расходятся в аналитическое хранилище (Hive/Presto), ML-пайплайны и системы fraud detection. Задержка от записи в OLTP до аналитики - менее 1 минуты.

  1. Поставить `wal_level = logical` в `postgresql.conf` и перезапустить PG
  2. Создать пользователя с ролью REPLICATION: `CREATE ROLE debezium REPLICATION LOGIN PASSWORD '...'`
  3. Выдать права на таблицы: `GRANT SELECT ON TABLE orders TO debezium`
  4. Создать PUBLICATION для нужных таблиц
  5. Запустить Debezium-коннектор - он сам создаст replication slot

В PostgreSQL для logical replication требуется параметр `wal_level`. Какое значение нужно установить?

CDC Patterns и применение

CDC используется в нескольких устойчивых паттернах. **Outbox Pattern** решает проблему двойной записи: сервис записывает бизнес-событие в таблицу `outbox` в той же транзакции, что и основное изменение, а CDC доставляет его в Kafka. Гарантия: либо оба действия произошли, либо ни одно.

**CQRS + CDC**: команды пишут в OLTP (PostgreSQL), CDC доставляет изменения в read-модели (Elasticsearch, Redis, ClickHouse). Каждая read-модель оптимизирована под свой паттерн запросов - без дублирования логики синхронизации.

  • **Event Sourcing via CDC** - MySQL binlog как неизменяемый лог событий; LinkedIn использует этот паттерн для Databus (предшественник Kafka)
  • **Cache Invalidation** - Redis-кеш сбрасывается точечно по CDC-событию, а не по TTL; снижает staleness без лишних запросов к БД
  • **Search Index Sync** - Elasticsearch индекс обновляется через CDC; Netflix так синхронизирует каталог фильмов между PostgreSQL и поиском
  • **Analytics ETL** - ClickHouse принимает CDC-поток и строит аналитические агрегаты в near-real-time вместо ночных батч-джобов

Критический момент: CDC гарантирует **at-least-once** доставку. Потребитель может получить одно событие дважды (при рестарте коннектора). Решение - идемпотентная обработка: использовать `id` события или LSN как ключ при вставке в целевую систему (`INSERT ... ON CONFLICT DO NOTHING`).

CDC - это просто ещё один способ делать ETL, только быстрее

CDC меняет парадигму: вместо периодического копирования состояния система получает непрерывный поток изменений. Это позволяет строить event-driven архитектуры, где downstream-системы реагируют на факты, а не опрашивают источник.

ETL работает со снимками данных и теряет промежуточные состояния (если запись была создана и удалена между двумя запусками ETL - ETL этого не увидит). CDC видит каждое изменение, что принципиально для аудита, event sourcing и near-real-time аналитики.

Сервис должен атомарно обновить запись в БД и опубликовать событие в Kafka. Без Outbox Pattern это проблема, потому что...

Итоги

  • **Log-based CDC** читает WAL (PostgreSQL) или binlog (MySQL) и превращает каждую транзакцию в событие - без polling, без дополнительной нагрузки на таблицы, с захватом DELETE
  • **Debezium + Kafka** - промышленный стандарт: коннектор читает replication slot, публикует события в топики, сохраняет позицию (LSN) - при рестарте продолжает с той же точки
  • **Outbox Pattern** через CDC решает проблему двойной записи атомарно: обновление БД и публикация события в одной транзакции, CDC доставляет событие асинхронно
  • **At-least-once delivery** требует идемпотентной обработки на стороне потребителя - использовать LSN или event ID как ключ дедупликации

Связанные темы

CDC связан с несколькими фундаментальными темами распределённых систем:

  • Kafka и event streaming — CDC-события публикуются в Kafka-топики; Kafka обеспечивает дурабельность и replay для downstream-потребителей
  • Outbox Pattern — CDC - транспортный уровень для Outbox: атомарная запись в БД + асинхронная доставка события без двухфазного коммита
  • CQRS — CDC синхронизирует write-модель (OLTP) с read-моделями (Elasticsearch, Redis, ClickHouse) - основа CQRS в распределённых системах
  • WAL и транзакционный журнал — CDC стоит поверх WAL - понимание структуры журнала помогает диагностировать задержки и проблемы с replication slots

Вопросы для размышления

  • Replication slot в PostgreSQL накапливает WAL, пока потребитель не прочитает. Как организовать мониторинг, чтобы не допустить переполнения диска при падении Debezium?
  • Downstream-сервис получил CDC-событие UPDATE дважды из-за рестарта коннектора. Какие конкретные механизмы идемпотентности применимы в зависимости от целевой системы (PostgreSQL, Elasticsearch, Redis)?
  • В каких сценариях CDC избыточен и проще использовать polling? Где граница между разумным polling и необходимостью CDC?

Связанные уроки

  • db-22-replication
Change Data Capture

0

1

Войти