Комментарии (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 среде, обеспечивающий надёжность, версионирование и воспроизводимость.