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

Что такое миграция?

1.0 Junior🔥 241 комментариев
#Другое

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

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

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

Миграция (Database Migration)

Миграция — это контролируемый и версионируемый способ изменения структуры базы данных (схемы). Миграция позволяет изменять таблицы, колонки, индексы и другие элементы БД предсказуемо и воспроизводимо, сохраняя историю всех изменений.

Миграция — это особенно важна в командной разработке и при развертывании приложения на разные среды (разработка, тестирование, production).

Основная концепция

Без миграций:
Разработчик А меняет БД вручную на своей машине
Разработчик Б не знает об изменениях
Проблемы при развёртывании на production

С миграциями:
Разработчик А создает миграцию (версионируемый SQL/код)
Миграция хранится в git
Разработчик Б автоматически применяет миграцию
На production автоматически применяются все миграции

Структура миграции

Папка migrations/:
  ├── 0001_create_users_table.sql
  ├── 0002_add_email_to_users.sql
  ├── 0003_create_posts_table.sql
  └── 0004_add_foreign_key.sql

База данных отслеживает:
  migrations_history:
    0001_create_users_table.sql     ✓ (примененa)
    0002_add_email_to_users.sql     ✓ (примененa)
    0003_create_posts_table.sql     ✓ (примененa)
    0004_add_foreign_key.sql        ○ (не применена)

Типы миграций

SQL-based миграции (Goose, Flyway)

-- migrations/0001_create_users_table.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- migrations/0002_add_age_column.sql
ALTER TABLE users ADD COLUMN age INT;

-- migrations/0003_drop_age_column.sql (откат)
ALTER TABLE users DROP COLUMN age;

Python-based миграции (Alembic, Django)

# alembic/versions/001_create_users_table.py
from alembic import op
import sqlalchemy as sa

def upgrade():
    op.create_table(
        'users',
        sa.Column('id', sa.Integer, primary_key=True),
        sa.Column('name', sa.String(255), nullable=False),
        sa.Column('email', sa.String(255), unique=True),
        sa.Column('created_at', sa.DateTime, default=sa.func.now())
    )

def downgrade():
    op.drop_table('users')
# Django миграция
# myapp/migrations/0001_initial.py
from django.db import migrations, models

class Migration(migrations.Migration):
    initial = True
    dependencies = []
    
    operations = [
        migrations.CreateModel(
            name='User',
            fields=[
                ('id', models.AutoField(primary_key=True)),
                ('name', models.CharField(max_length=255)),
                ('email', models.EmailField(unique=True)),
            ],
        ),
    ]

Инструменты для миграций

Goose (рекомендуется для PostgreSQL)

# Инсталляция
go install github.com/pressly/goose/v3/cmd/goose@latest

# Создание миграции
goose create add_users_table sql

# Применение миграций
goose postgres "user=postgres dbname=mydb sslmode=disable" up

# Откат последней миграции
goose postgres "user=postgres dbname=mydb sslmode=disable" down

# Статус миграций
goose postgres "user=postgres dbname=mydb sslmode=disable" status

Alembic (Python)

# Инсталляция
pip install alembic

# Инициализация
alembic init migrations

# Создание автоматической миграции
alembic revision --autogenerate -m "Add users table"

# Применение
alembic upgrade head

# Откат
alembic downgrade -1

Django Migrations

# Создание миграции из моделей
python manage.py makemigrations

# Применение миграций
python manage.py migrate

# Просмотр SQL для миграции
python manage.py sqlmigrate myapp 0001

# Откат миграции
python manage.py migrate myapp 0000

Жизненный цикл миграции

1. РАЗРАБОТКА
   ├─ Разработчик изменяет модель/схему
   └─ Создаёт миграцию

2. ВЕРСИОНИРОВАНИЕ
   ├─ Миграция в git
   └─ Коммит и PR

3. РЕВЬЮ
   ├─ Другие разработчики проверяют SQL
   └─ Одобрение

4. ПРИМЕНЕНИЕ
   ├─ На разработку
   ├─ На staging (тестирование)
   └─ На production

5. МОНИТОРИНГ
   ├─ Проверка успешности
   └─ Откат если нужно

Практический пример

# models.py
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True)
    name = Column(String(255), nullable=False)
    email = Column(String(255), unique=True, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)

class Post(Base):
    __tablename__ = "posts"
    
    id = Column(Integer, primary_key=True)
    title = Column(String(255), nullable=False)
    content = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)
-- migrations/0001_initial.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    user_id INTEGER NOT NULL REFERENCES users(id),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_posts_user_id ON posts(user_id);

Лучшие практики

1. Пиши миграции расчётливо

-- ✅ Хорошо: атомарные изменения
-- migrations/0001_add_status_column.sql
ALTER TABLE users ADD COLUMN status VARCHAR(50);

-- ✅ Хорошо: с дефолтным значением
ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT true;

-- ❌ Плохо: бизнес-логика в миграции
ALTER TABLE users ADD COLUMN score INT;
UPDATE users SET score = (SELECT COUNT(*) FROM posts WHERE posts.user_id = users.id);

2. Версионирование миграций

✅ Хорошо:
0001_create_users_table.sql
0002_add_email_column.sql
0003_create_posts_table.sql

❌ Плохо:
v1.sql
latest.sql
final.sql

3. Тестируй миграции

# test_migrations.py
import pytest
from alembic.config import Config
from alembic.command import upgrade, downgrade

def test_migration_up():
    config = Config("alembic.ini")
    upgrade(config, "head")  # Применить все
    # Проверить результаты

def test_migration_down():
    upgrade(config, "head")
    downgrade(config, "-1")  # Откатить одну
    # Проверить откат

4. Не удаляй миграции

✅ Правильно: новая миграция для отката
0001_add_column.sql   ✓ (применена на production)
0002_remove_column.sql ✓ (откатывает 0001)

❌ Неправильно: удаление старой миграции
(Нарушит историю на production)

5. Откат к безопасности

# ❌ Опасно: теряется данные
DROP TABLE users;

# ✅ Безопаснее: переименование с последующей очисткой
ALTER TABLE users RENAME TO users_backup;
-- После проверки что всё работает:
-- DROP TABLE users_backup;

Обратная совместимость

-- Этап 1: Добавляем новую колонку (production работает)
ALTER TABLE users ADD COLUMN new_field VARCHAR(255);

-- Этап 2: Приложение начинает писать в новую колонку
-- (старый код читает из old_field)

-- Этап 3: Приложение полностью переходит на new_field

-- Этап 4: Удаляем старую колонку
ALTER TABLE users DROP COLUMN old_field;

Миграции в команде

1. Разработчик А создает миграцию
   git add migrations/0005_new_feature.sql
   git commit -m "Add new feature migration"
   git push

2. Разработчик Б получает код
   git pull
   goose up  # Автоматически применяет 0005

3. На production
   git pull
   goose up  # Применяет все неприменённые миграции
   systemctl restart myapp  # Перезапуск приложения

Миграция — это критически важный инструмент для управления изменениями БД в production среде, обеспечивающий надёжность, версионирование и воспроизводимость.

Что такое миграция? | PrepBro