Проблема двойной записи возникает, когда необходимо выполнить две или более операции в разных системах или базах данных, которые должны оставаться согласованными, а в системе при этом нет встроенной поддержки транзакций для обеспечения согласованности. Классический пример – когда нужно записать данные в базу данных и опубликовать событие в брокере событий. Если приложение завершается аварийно после завершения транзакции в базе данных, но до того, как событие будет опубликовано, это приведет к несогласованности, поскольку операции происходят в отдельных системах, и транзакции в этом контексте недоступны:
Существует два стандартных решения для обработки этой проблемы – захват изменения данных и паттерн Outbox.
Захват изменения данных (CDC)
Захват изменения данных (CDC, Change Data Capture) – это метод, при котором используются журналы транзакций базы данных, содержащие записи обо всех изменениях данных. CDC-инструменты могут читать эти журналы, надежно захватывая все операции вставки, обновления или удаления. Эти данные затем передаются в другие системы, например брокеры сообщений, чтобы обеспечить информирование остальных систем о произошедших изменениях:
Есть два основных способа реализовать захват изменений:
- Нативные CDC-функции базы данных. Многие базы данных имеют встроенные CDC-возможности, например, binlog в MySQL или логическая репликация в PostgreSQL. Эти решения обычно проще интегрировать с приложением.
- Инструменты сторонних производителей. Например, опенсорсный Debezium может подключаться к различным базам данных и отлично работает в разнородных системах.
Преимущества CDC:
- CDC-инструмент берет на себя публикацию событий, освобождая приложение от необходимости управлять процессом.
- CDC тесно связан с базой данных, и это гарантирует, что зафиксированные транзакции будут захвачены и опубликованы как события.
- Архитектуры, основанные на CDC, часто проще масштабировать, так как приложение не управляет одновременно изменениями данных и публикацией событий.
Недостатки:
- Настройка и управление инструментами CDC добавляет операционную сложность.
- Хотя CDC обычно захватывает изменения в реальном времени, небольшая задержка между моментом фиксации изменения и его публикацией в нисходящей части системы все-таки возможна. Это важно учитывать, если ваше приложение требует минимальной задержки.
- Для того чтобы CDC-пайплайн оставался работоспособным, необходимо обеспечить надежный мониторинг и обработку экстремальных случаев (изменения схемы, перезапуск базы данных и т. п.)
Паттерн Outbox
Паттерн Outbox помогает избежать проблемы двойной записи, сохраняя согласованность между изменением данных и публикацией событий. В этом подходе события сохраняются в специальной таблице Outbox (исходящие события) как часть той же транзакции, что и изменение данных. Отдельный процесс (воркер или фоновая задача) периодически проверяет таблицу Outbox на наличие новых событий. Эти события извлекаются и публикуются в брокере сообщений (Kafka, RabbitMQ):
Если транзакция завершается успешно, изменения в данных и запись события фиксируются вместе. Если что-то пойдет не так, обе операции откатываются.
Варианты реализации паттерна Outbox:
- Механизм опроса (поллинг). Фоновый процесс регулярно проверяет таблицу Outbox на новые записи и публикует их.
- Чтение журналов транзакций. Воркер может следить за изменениями в журнале транзакций, чтобы обрабатывать новые записи в таблице Outbox (этот метод используется реже).
Преимущества паттерна Outbox:
- Атомарность. Поскольку изменения данных и запись события происходят в одной транзакции, либо обе операции успешны, либо обе откатываются. Это исключает несогласованность.
- Гарантированная публикация. Даже если приложение или воркер выйдет из строя, событие не потеряется – асинхронный процесс обработает его позже, сохраняя согласованность.
- Основная логика приложения остается простой, так как публикация событий отделена и происходит асинхронно.
Недостатки:
- Требуется организовать управление таблицей Outbox – реализовать запуск воркера или фоновой задачи для проверки и публикации событий.
- Увеличение таблицы Outbox может привести к снижению производительности. Нужно настроить периодические очистки.
- В зависимости от частоты опроса таблицы или расписания фонового процесса, между фиксацией данных и публикацией события может быть задержка.
- Необходимо учитывать сценарии отказов, например, повторную обработку событий.
Подведем итоги
Проблема двойной записи возникает в распределенных системах, когда данные необходимо одновременно сохранить в нескольких хранилищах или базах данных, но нет возможности сделать это атомарно, в рамках единой неделимой транзакции. Самые популярные способы решения этой проблемы – инструменты для захвата изменений и паттерн исходящих событий Outbox. Они усложняют архитектуру системы, зато сводят к минимуму последствия сбоев приложения и гарантируют согласованность данных.
С какими проблемами синхронизации данных вы сталкивались в своих проектах и как их решали?
Комментарии