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

Как репликация помогает увеличить скорость?

2.2 Middle🔥 241 комментариев
#DevOps и инфраструктура#Архитектура и паттерны#Базы данных (SQL)

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

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

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

Репликация базы данных и увеличение скорости

Репликация — это технология копирования данных с одного сервера БД (master/primary) на один или несколько других (slaves/replicas). Она критична для масштабирования приложений.

1. Основные концепции

Master (Primary) Server
    ↓ (Write Query)
  Binlog (Binary Log)
    ↓ (Replication Stream)
Slave 1 (Read Replica)
Slave 2 (Read Replica)
Slave 3 (Read Replica)

Master-Slave архитектура

  • Master: принимает запросы на запись (INSERT, UPDATE, DELETE)
  • Slaves: получают копию данных и обслуживают запросы на чтение (SELECT)
  • Binlog: лог всех изменений на master

2. Типы репликации

Statement-based Replication

-- На master
UPDATE users SET status='active' WHERE created_at > '2024-01-01';

-- На slave выполняется тот же SQL statement
UPDATE users SET status='active' WHERE created_at > '2024-01-01';

Плюсы: меньше данных в binlog Минусы: может быть недетерминированно (RAND(), NOW())

Row-based Replication

-- Master логирует изменение каждой строки
Binlog Entry: UPDATE user_id=1 SET status='active'
Binlog Entry: UPDATE user_id=2 SET status='active'
Binlog Entry: UPDATE user_id=3 SET status='active'

Плюсы: точная репликация Минусы: больше данных в binlog

3. Как репликация увеличивает скорость

3.1 Распределение нагрузки на чтение

from django.db import connections

# Все запросы на запись идут на master
user = User.objects.create(name="Alice")  # На master

# Запросы на чтение распределяются на slaves
users = User.objects.using('replica_1').all()  # На slave 1
user_detail = User.objects.using('replica_2').get(id=1)  # На slave 2

3.2 Балансировка нагрузки

import random
from django.conf import settings

class ReadWriteRouter:
    """Router для распределения запросов"""
    
    def db_for_read(self, model, **hints):
        # Случайный replica для чтения
        replicas = ['replica_1', 'replica_2', 'replica_3']
        return random.choice(replicas)
    
    def db_for_write(self, model, **hints):
        # Всегда на master
        return 'master'
    
    def allow_relation(self, obj1, obj2, **hints):
        return True
    
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        return db == 'master'  # Миграции только на master

3.3 Задержка репликации

# Обычно slave отстаёт на 100-1000ms

from django.core.cache import cache
from django.utils import timezone

def get_user_data(user_id):
    # Недавно обновленные данные читаем с master
    user = User.objects.using('master').get(id=user_id)
    
    # Старые данные читаем с replica (быстрее)
    if timezone.now() - user.updated_at > timedelta(seconds=5):
        user = User.objects.using('replica').get(id=user_id)
    
    return user

4. Настройка репликации PostgreSQL

-- На master
-- postgresql.conf
wal_level = replica  # Включить WAL
max_wal_senders = 10
wal_keep_size = 1GB

-- Создать пользователя для репликации
CREATE ROLE replicator WITH REPLICATION ENCRYPTED PASSWORD 'password';

-- На slave
-- Остановить PostgreSQL
sudo systemctl stop postgresql

-- Выполнить базовую копию
pg_basebackup -h master_ip -D /var/lib/postgresql/data -U replicator -v -P --wal-method=stream

-- Включить recovery.conf
echo "standby_mode = 'on'" >> /var/lib/postgresql/data/recovery.conf
echo "primary_conninfo = 'host=master_ip port=5432 user=replicator password=password'" >> /var/lib/postgresql/data/recovery.conf

-- Запустить PostgreSQL
sudo systemctl start postgresql

5. Настройка репликации MySQL

-- На master (my.cnf)
server-id = 1
log_bin = mysql-bin
binlog_format = ROW

-- Создать пользователя для репликации
CREATE USER 'replicator'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%';
FLUSH PRIVILEGES;

-- Получить binlog position
SHOW MASTER STATUS;

-- На slave (my.cnf)
server-id = 2
relay-log = mysql-relay-bin

-- Настроить репликацию
CHANGE MASTER TO
  MASTER_HOST='master_ip',
  MASTER_USER='replicator',
  MASTER_PASSWORD='password',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=123;

START SLAVE;
SHOW SLAVE STATUS;

6. Практические примеры с Django

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'master.db.example.com',
        'PORT': 5432,
    },
    'replica_1': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'replica1.db.example.com',
        'PORT': 5432,
    },
    'replica_2': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'replica2.db.example.com',
        'PORT': 5432,
    },
}

DATABASE_ROUTERS = ['myapp.routers.ReadWriteRouter']

# Кэширование для стабильности данных
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
    }
}

7. Обработка задержки репликации

from django.core.cache import cache
from django.utils import timezone

class UserService:
    REPLICATION_LAG = 2  # секунды
    
    def get_user_after_create(self, user_id):
        # Если данные только что изменились, читаем с master
        cache_key = f"user_modified:{user_id}"
        if cache.get(cache_key):
            return User.objects.using('master').get(id=user_id)
        
        # Иначе читаем с replica
        return User.objects.using('replica').get(id=user_id)
    
    def create_user(self, name, email):
        user = User.objects.create(name=name, email=email)
        
        # Отметить, что данные только что изменились
        cache.set(f"user_modified:{user.id}", True, self.REPLICATION_LAG)
        
        return user

8. Мониторинг репликации

def check_replication_lag(replica_alias='replica'):
    """Проверить задержку репликации"""
    from django.db import connections
    
    conn = connections[replica_alias]
    cursor = conn.cursor()
    
    if 'postgresql' in conn.settings_dict['ENGINE']:
        cursor.execute('SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();')
        result = cursor.fetchone()
        # Анализировать результат
    
    elif 'mysql' in conn.settings_dict['ENGINE']:
        cursor.execute('SHOW SLAVE STATUS;')
        status = cursor.fetchone()
        lag_seconds = status['Seconds_Behind_Master']
        print(f"Replication lag: {lag_seconds} seconds")

9. Best Practices

1. Избегайте hot spots: распределяйте читатель по нескольким replicas 2. Следите за задержкой: используйте мониторинг для отслеживания lag 3. Обработка lag: кэшируйте свежие данные после записи 4. Несколько replicas: минимум 2-3 для redundancy 5. Failover: готовьте план переключения если master упадёт 6. Настройка: используйте ROW-based репликацию для точности

10. Производительность

Без репликации:

1 master: 100 requests/sec
Ограничение: disk I/O

С репликацией (1 master + 3 slaves):

1 master: 100 writes/sec
3 slaves: 300 reads/sec
Общая пропускная способность: 400 requests/sec
Улучшение: 4x
Как репликация помогает увеличить скорость? | PrepBro