> Какие виды репликаций существуют (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Employcity

Стек: Go

> Пример ответа

Короткий ответ

Репликация в базах данных бывает трёх основных видов: single-leader (master-slave), multi-leader и leaderless. В single-leader все записи идут через один узел, чтение - с реплик. В multi-leader несколько узлов принимают записи, что даёт лучшую доступность, но требует разрешения конфликтов. В leaderless (например, Dynamo-style) любой узел принимает записи, консистентность обеспечивается через quorum. Также различают синхронную и асинхронную репликацию, и по способу передачи - statement-based, WAL-based и row-based.

Подробное объяснение

Репликация решает три задачи: отказоустойчивость, масштабирование чтения и географическая распределённость. Основные модели:

Single-leader (master-slave)

  • Один узел (leader) принимает все записи, остальные (followers) копируют изменения.
  • Чтение можно масштабировать горизонтально, но запись ограничена одним узлом.
  • Если leader падает, нужен failover - выбор нового лидера, что может привести к потере данных при асинхронной репликации.

Multi-leader

  • Несколько лидеров принимают записи, каждый реплицирует свои изменения остальным.
  • Используется в multi-datacenter сценариях, где важна локальная запись.
  • Главная проблема - конфликты записи (например, два лидера обновили одну строку). Решения: last-write-wins, merge, CRDT.

Leaderless

  • Любой узел принимает чтение и запись, консистентность через quorum (например, W + R > N).
  • Примеры: Cassandra, Riak, DynamoDB.
  • Хорошо переносит отказы узлов, но требует аккуратной настройки quorum и разрешения конфликтов на чтении (read repair, hinted handoff).

По синхронности

  • Синхронная: leader ждёт подтверждения от реплики перед ответом клиенту. Гарантирует отсутствие потерь, но увеличивает задержку.
  • Асинхронная: leader отвечает сразу, репликация идёт в фоне. Быстрее, но возможна потеря данных при сбое лидера.
  • Semi-synchronous: компромисс - ждём подтверждения от одной реплики.

По механизму передачи

  • Statement-based: передаются SQL-запросы. Просто, но недетерминированные функции (NOW(), RAND()) дают расхождения.
  • WAL-based: передаётся write-ahead log. Надёжно, но привязывает к формату хранения.
  • Row-based: передаются изменённые строки. Универсально, используется в MySQL binlog, PostgreSQL logical replication.

На практике

В Go-проектах репликация чаще всего настраивается на уровне БД, а не приложения. Но важно понимать, как она влияет на код:

  • При использовании single-leader нужно разделять пулы соединений: записи - на leader, чтения - на followers. В Go это делается через отдельные sql.DB или через middleware типа pgx с указанием роли.
  • Для чтения с реплик нужно учитывать репликационную задержку. Если пользователь только что записал данные и сразу читает, он может не увидеть их на follower. Решение - read-your-writes consistency: для критичных запросов читать с leader.
  • В multi-leader сценариях в Go-приложении нужно обрабатывать конфликты на уровне бизнес-логики, например, через версионирование или CRDT.
  • При использовании leaderless (например, Cassandra) в Go-драйвере настраивается consistency level: QUORUM, ONE, LOCAL_QUORUM. Это влияет на задержку и консистентность.

Пример кода

Пример разделения чтения и записи в Go с PostgreSQL:

GO
type DB struct {
write *sql.DB
read *sql.DB
}
func NewDB(writeDSN, readDSN string) (*DB, error) {
write, err := sql.Open("pgx", writeDSN)
if err != nil {
return nil, err
}
read, err := sql.Open("pgx", readDSN)
if err != nil {
return nil, err
}
return &DB{write: write, read: read}, nil
}
func (d *DB) CreateUser(ctx context.Context, u User) error {
_, err := d.write.ExecContext(ctx, "INSERT INTO users (name) VALUES ($1)", u.Name)
return err
}
func (d *DB) GetUser(ctx context.Context, id int, readYourWrites bool) (*User, error) {
db := d.read
if readYourWrites {
db = d.write
}
row := db.QueryRowContext(ctx, "SELECT id, name FROM users WHERE id = $1", id)
// ...
}

Как отвечать на собеседовании

Начни с краткой классификации, затем углубляйся в зависимости от вопросов. Обязательно упомяни trade-off между консистентностью и доступностью, особенно в контексте CAP-теоремы. Приведи примеры из реальных систем: PostgreSQL (single-leader), MySQL Group Replication (multi-leader), Cassandra (leaderless). Покажи понимание, как репликация влияет на приложение: read-your-writes, задержки, обработка конфликтов. Если спросят про Go - расскажи про разделение пулов соединений и настройку драйверов.

Что проверяет интервьюер

  • Понимание базовых моделей репликации и их отличий.
  • Умение объяснить trade-off между синхронной и асинхронной репликацией.
  • Знание механизмов разрешения конфликтов в multi-leader и leaderless.
  • Понимание влияния репликации на проектирование приложения (консистентность чтения, задержки).
  • Способность применить знания к конкретному стеку (Go, PostgreSQL, Cassandra).

Типичные ошибки

  • Смешивание понятий "репликация" и "шардирование" - это разные вещи.
  • Утверждение, что multi-leader всегда лучше single-leader - на самом деле он добавляет сложность с конфликтами.
  • Игнорирование репликационной задержки при чтении с followers.
  • Непонимание, что синхронная репликация не гарантирует отсутствие потерь при network partition.
  • В Go-коде - использование одного пула соединений для чтения и записи без учёта роли узла.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью