> Как реализована репликация таблиц (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: Сбер
Стек: Go
> Пример ответа
Короткий ответ
Репликация таблиц - это механизм синхронного или асинхронного копирования изменений данных с primary-реплики на standby-реплики. В Go-экосистеме она реализуется через логическую репликацию (например, PostgreSQL logical decoding) или через паттерн outbox с публикацией событий в брокер. Ключевые аспекты: гарантии консистентности, обработка конфликтов, мониторинг лага репликации и идемпотентность применения изменений на стороне реплики.
Подробное объяснение
Репликация таблиц бывает двух основных типов:
Физическая репликация - копирование WAL (write-ahead log) на уровне байтов. Реплика получает точную копию данных, включая схему и внутреннее представление. Используется для failover и read scaling. В Go обычно не требует специального кода - управляется на уровне СУБД.
Логическая репликация - декодирование WAL в поток изменений строк (INSERT/UPDATE/DELETE). Позволяет:
- реплицировать только выбранные таблицы
- трансформировать данные по пути
- реплицировать между разными версиями СУБД
В Go логическая репликация реализуется через:
pglogical/pgoutputдля PostgreSQL- библиотеки типа
jackc/pglogreplдля подписки на изменения - паттерн outbox: запись изменений в отдельную таблицу внутри той же транзакции, затем публикация в Kafka/NATS через отдельный процесс
Ключевые trade-off:
- Синхронная vs асинхронная: синхронная гарантирует отсутствие потерь при failover, но увеличивает latency записи. Асинхронная быстрее, но возможна потеря последних транзакций.
- Идемпотентность: реплика должна корректно обрабатывать повторную доставку одного и того же события.
- Схема: при изменении схемы таблицы (DDL) необходимо согласованное обновление на всех репликах.
- Конфликты: при multi-primary репликации возможны конфликты записей - нужна стратегия разрешения (last-write-wins, merge, отказ).
На практике
Для Go-сервисов типичная схема:
- Основной сервис пишет в PostgreSQL.
- Отдельный Go-процесс (replicator) подписан на логическую репликацию через
pgoutput. - Полученные изменения сериализуются в protobuf/JSON и публикуются в Kafka.
- Потребители (search index, cache, аналитика) применяют изменения с идемпотентной обработкой.
Важные аспекты на практике:
- Лаг репликации - метрика, показывающая разницу между текущим WAL-позицией primary и позицией реплики. Нужен алертинг.
- Backpressure - если потребитель медленный, репликатор должен уметь притормаживать чтение из WAL, не теряя данные.
- Graceful shutdown - при остановке репликатор должен корректно сохранить свою LSN-позицию.
- Мониторинг - через Prometheus: количество обработанных событий, ошибки декодирования, время обработки.
Пример кода
Упрощённый пример подписки на логическую репликацию PostgreSQL на Go:
GOpackage mainimport ("context""fmt""log""github.com/jackc/pglogrepl""github.com/jackc/pgx/v5/pgconn")func main() {conn, err := pgconn.Connect(context.Background(), "postgres://user:pass@localhost/db")if err != nil {log.Fatal(err)}defer conn.Close(context.Background())plugin := "pgoutput"opts := pglogrepl.StartReplicationOptions{PluginArgs: []string{"proto_version '1'","publication_names 'my_publication'",},}err = pglogrepl.StartReplication(context.Background(),conn,"my_replication_slot",0, // LSN: 0 означает "с текущей позиции"opts,)if err != nil {log.Fatal(err)}for {msg, err := conn.ReceiveMessage(context.Background())if err != nil {log.Fatal(err)}switch m := msg.(type) {case *pgconn.CopyData:switch pglogrepl.MessageType(m.Data[0]) {case pglogrepl.PrimaryKeepaliveMessageID:// keepalive - можно ответить standby status updatecase pglogrepl.XLogDataID:xld, err := pglogrepl.ParseXLogData(m.Data[1:])if err != nil {log.Fatal(err)}processChanges(xld.WALData)}}}}func processChanges(data []byte) {// Декодирование pgoutput-сообщений// и применение к целевой системеfmt.Printf("received %d bytes of changes\n", len(data))}
Как отвечать на собеседовании
Начните с классификации: физическая vs логическая репликация. Затем переходите к конкретике для Go: как вы строите пайплайн, какие библиотеки используете, как обеспечиваете идемпотентность. Обязательно упомяните мониторинг лага и обработку сбоев.
Если спрашивают про конкретную СУБД - уточните, про какую именно. Для PostgreSQL расскажите про pgoutput и publication/slot. Для MySQL - про binlog и go-mysql-org/go-mysql.
Покажите понимание trade-off: синхронная репликация дороже, но надёжнее. Асинхронная быстрее, но требует компенсационных механизмов.
Что проверяет интервьюер
- Понимание разницы между физической и логической репликацией.
- Знание практических проблем: лаг, идемпотентность, обработка DDL.
- Умение проектировать отказоустойчивый пайплайн.
- Понимание гарантий консистентности (at-least-once, exactly-once).
- Опыт работы с реальными инструментами (pglogrepl, Kafka, Debezium).
Типичные ошибки
- Путаница между репликацией и шардированием.
- Утверждение, что логическая репликация даёт exactly-once доставку - на практике это at-least-once, идемпотентность нужно реализовывать самостоятельно.
- Игнорирование проблемы лага репликации и его влияния на чтение.
- Отсутствие стратегии обработки конфликтов при multi-primary.
- Предложение использовать физическую репликацию для трансформации данных - это не её задача.
- Забывают про необходимость обработки DDL-изменений в схеме таблиц.
> Похожие задачи по backend
Почему Kafka такая быстрая
Могут ли процессы и потоки общаться между собой и как
Работали ли вы с репликацией таблиц
Столкнулись ли вы с проблемами нагруженности таблиц и как их решали
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью