← Назад к вопросам

Что произойдёт если упадёт master-slave?

1.3 Junior🔥 191 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Общий ответ на вопрос

Если упадет master-slave репликация в Go-приложении (чаще всего речь о MySQL/PostgreSQL), последствия зависят от типа отказа и архитектуры приложения. Падение мастера или проблемы с репликацией приводят к каскадным сбоям.

Основные сценарии и последствия

1. Отказ мастера (Master failure)

Это наиболее критичный сценарий. Мастер отвечает за запись (write) данных.

Последствия:

  • Все операции записи (INSERT/UPDATE/DELETE) прекращаются → приложение теряет функциональность
  • Транзакции прерываются, возможна потеря данных из кэша транзакций
  • Автоинкрементные ключи могут сгенерировать дубликаты или разрывы
  • Если приложение не имеет механизма автоматического переключения (failover), требуется ручное вмешательство

Пример проблемы в коде:

// Типичная ошибка - хардкод мастера
func SaveOrder(db *sql.DB, order Order) error {
    // Если master-подключение "протухло", запись упадет
    _, err := db.Exec("INSERT INTO orders (...) VALUES (...)")
    if err != nil {
        // Все запросы начинают фейлиться
        return fmt.Errorf("failed to save order: %w", err)
    }
    return nil
}

2. Проблемы с репликацией (Replication lag/failure)

Когда slave перестает синхронизироваться с мастером или отстает.

Последствия:

  • Несогласованность данных (data inconsistency) - чтение из slave возвращает устаревшие данные
  • Актуальные данные недоступны для read-only операций
  • Приложения с высокой требовательностью к консистентности дают неправильные результаты
  • Могут возникать race conditions в бизнес-логике
func GetUserBalance(userID int) (float64, error) {
    var balance float64
    // Читаем из slave (реплики)
    err := slaveDB.QueryRow("SELECT balance FROM users WHERE id = ?", userID).Scan(&balance)
    
    // Если репликация отстает, пользователь увидит старый баланс
    // после только что выполненного перевода
    return balance, err
}

3. Каскадные отказы реплик (Slave cascade failure)

Если slave зависит от другого slave или все реплики зависят от одного мастера.

Решения и архитектурные паттерны

1. Мониторинг и автоматический failover

// Использование health checks для мониторинга
func checkMasterHealth(masterDSN string) bool {
    db, err := sql.Open("mysql", masterDSN)
    if err != nil {
        return false
    }
    defer db.Close()
    
    // Простой запрос для проверки живости
    var result int
    err = db.QueryRow("SELECT 1").Scan(&result)
    return err == nil && result == 1
}

2. Использование пулов соединений с маршрутизацией

// Паттерн Database Router
type DBRouter struct {
    master *sql.DB
    slaves []*sql.DB
    currentSlaveIndex int
    mu sync.RWMutex
}

func (r *DBRouter) GetReadConn() *sql.DB {
    r.mu.RLock()
    defer r.mu.RUnlock()
    
    // Round-robin или health-based выбор slave
    if len(r.slaves) == 0 {
        return r.master // Fallback на мастер
    }
    
    idx := r.currentSlaveIndex % len(r.slaves)
    r.currentSlaveIndex++
    return r.slaves[idx]
}

func (r *DBRouter) GetWriteConn() *sql.DB {
    return r.master
}

3. Circuit breaker для устойчивости

// Паттерн Circuit Breaker для обработки сбоев
type DBCircuitBreaker struct {
    failures    int
    maxFailures int
    resetTimeout time.Duration
    lastFailure time.Time
    mu sync.Mutex
}

func (cb *DBCircuitBreaker) Execute(op func() error) error {
    cb.mu.Lock()
    
    // Проверка состояния circuit breaker
    if cb.failures >= cb.maxFailures {
        if time.Since(cb.lastFailure) < cb.resetTimeout {
            cb.mu.Unlock()
            return fmt.Errorf("database unavailable (circuit open)")
        }
        // Сброс после таймаута
        cb.failures = 0
    }
    cb.mu.Unlock()
    
    err := op()
    if err != nil {
        cb.mu.Lock()
        cb.failures++
        cb.lastFailure = time.Now()
        cb.mu.Unlock()
    }
    
    return err
}

Рекомендации для Go-разработчика

  1. Всегда проектируйте приложение с учетом отказов БД
  2. Используйте готовые решения:
    • github.com/go-sql-driver/mysql с настройкой allowFallback
    • ProxySQL или HAProxy для автоматического фаиловера
    • Виртуализация IP-адресов (Virtual IP)
  3. Реализуйте многоуровневое кэширование (Redis, Memcached) для снижения нагрузки на БД
  4. Настройте алертинг и мониторинг:
    • Replication lag
    • Connections count
    • Query performance
  5. Планируйте отказы:
    • Регулярные тесты failover
    • Backup и restore процедуры
    • План аварийного восстановления (DRP)

Вывод

Падение master-slave репликации — не вопрос "если", а вопрос "когда". В Go-экосистеме критически важно:

  • Не доверять единственному подключению к БД
  • Внедрять resilience patterns (retry, circuit breaker, fallback)
  • Мониторить состояние репликации в реальном времени
  • Иметь автоматические или полуавтоматические процедуры восстановления

Современные подходы склоняются к использованию multi-master репликации, кластерных решений (CockroachDB, Vitess) или бессерверных БД, которые уменьшают риски традиционной master-slave архитектуры.

Что произойдёт если упадёт master-slave? | PrepBro