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

Когда следует использовать master-slave в БД?

2.0 Middle🔥 201 комментариев
#Python Core#Архитектура и паттерны

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

Когда использовать master-slave репликацию в БД

Мaster-slave (Primary-Replica) репликация — это архитектура, где главная БД (master) реплицируется на одну или несколько подчинённых (slave). Это критично для масштабируемости и надёжности.

Основной механизм

Мaster DB (Write)
    ↓
  WAL (Write-Ahead Log)
    ↓
Slave 1 DB (Read)
Slave 2 DB (Read)
Slave 3 DB (Read)

Все записи идут в master, все чтения могут идти в slave.

Когда использовать

1. Высокие объёмы чтений

Если у вас 1000 чтений на 10 записей, один master удовлетворит потребности:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# Master для записей
master_engine = create_engine('postgresql://localhost/mydb')

# Slaves для чтений
read_engines = [
    create_engine('postgresql://slave1/mydb'),
    create_engine('postgresql://slave2/mydb'),
]

def get_user(user_id):
    # Читаем со случайного slave
    import random
    engine = random.choice(read_engines)
    session = sessionmaker(bind=engine)()
    return session.query(User).filter_by(id=user_id).first()

def create_user(email):
    # Пишем в master
    session = sessionmaker(bind=master_engine)()
    user = User(email=email)
    session.add(user)
    session.commit()
    return user

2. Масштабирование под бэкапы и аналитику

# Slave используется для:
# - Полного бэкапа (без блокировки master)
# - Аналитических запросов (тяжелые SELECT)
# - Тестирования (копия production)

mysqldump --single-transaction \
  --master-data=2 \
  -u root -p database > backup.sql

3. Высокая доступность и failover

# Если master упадёт, promocе slave
# Используйте инструменты типа MySQL Orchestrator

import pymysql

def write_with_failover(query):
    try:
        master_conn = pymysql.connect('master_host')
        master_conn.query(query)
    except Exception as e:
        print(f"Master failed: {e}")
        # Промотируем slave
        slave_conn = pymysql.connect('slave_host')
        slave_conn.query("STOP SLAVE;")
        slave_conn.query("RESET MASTER;")
        # Теперь slave стал master

4. Геораспределённые системы

Мaster в EU, slaves в US, Asia:

User in US
    ↓
Read → Slave (US) ← Репликация ← Master (EU)
             (2ms)                  (100ms)
             
Вместо:
Read → Master (EU) (100ms)

Когда НЕ использовать

1. Маленькие приложения

Оверхед на репликацию больше, чем выигрыш:

# Если вы имеете:
# - 100 пользователей
# - 1 запрос в минуту
# Репликация ненужна

2. Требования сильной консистентности

Eslave отстаёт на миллисекунды/секунды:

# ❌ ПЛОХО
user = create_user(email)  # Пишем в master
user_data = get_user(user.id)  # Читаем со slave
# user_data может быть None, если slave ещё не синхронизировался

# ✅ ХОРОШО
user = create_user(email)  # Пишем в master
# Читаем из master на время
session = sessionmaker(bind=master_engine)()
user_data = session.query(User).filter_by(id=user.id).first()

3. Частые изменения схемы

Миграции нужно синхронизировать:

# На master
ALTER TABLE users ADD COLUMN age INT;

# Slave автоматически получит эту команду
# Но может отстать, если она тяжелая

Проблемы и решения

Проблема: Лаг репликации (Replication Lag)

# Slave отстаёт на 2 секунды
import time

def safe_read_after_write(user_id):
    # Записываем в master
    user = create_user(email)
    
    # Ждём синхронизации
    time.sleep(2)
    
    # Читаем со slave
    return get_user(user_id)

Или используйте read-after-write consistency:

def get_user_safe(user_id, master=False):
    # Если нужна гарантия, читаем из master
    if master:
        engine = master_engine
    else:
        engine = random.choice(read_engines)
    
    session = sessionmaker(bind=engine)()
    return session.query(User).filter_by(id=user_id).first()

Проблема: Split Brain (два master)

# Сетевой раздел между master и slave
# Оба начинают принимать писки
# Данные расходятся

# Решение: использовать fencing
# Автоматически отключить старый master

PostgreSQL репликация

# На master (postgresql.conf)
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB

# На slave
standby_mode = on
restore_command = 'cp /archive/%f %p'

# Проверка статуса
psql -c "SELECT * FROM pg_stat_replication;"

MySQL репликация

# На master
BINARY FORMAT = ROW
BINLOG_EXPIRE_LOGS_DAYS = 7

# На slave
CHANGE MASTER TO
  MASTER_HOST = 'master_host',
  MASTER_USER = 'replication_user',
  MASTER_PASSWORD = 'password',
  MASTER_LOG_FILE = 'mysql-bin.000001',
  MASTER_LOG_POS = 154;

START SLAVE;
SHOW SLAVE STATUS;

Итоги

  • Используйте master-slave, если читаете в 10+ раз больше, чем пишете
  • Усложняет архитектуру — нужна обработка лага репликации
  • Решает масштабирование под чтения
  • Не решает масштабирование под записи (нужно sharding)
  • Требует мониторинга — лаг, failover, консистентность