← Назад к вопросам
Что такое Migrations в БД?
1.0 Junior🔥 251 комментариев
#DevOps и инфраструктура#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое Migrations в БД?
Migrations (миграции) — это версионный контроль для схемы базы данных. Это как git для структуры таблиц, но для БД. Я использую их ежедневно в production коде.
Проблема без миграций
Деньги 1: Создал таблицу users вручную
CREATE TABLE users (id INT, name VARCHAR(100));
День 2: Добавил email колонку вручную
ALTER TABLE users ADD COLUMN email VARCHAR(100);
День 3: Хочу дать это другому разработчику
Как ему передать эту информацию?
- Отправить SQL скрипт? (запутано)
- Сказать что-то вроде "выполни эти команды в БД"? (неправильно)
- Дать дамп БД? (ОГРОМНЫЙ файл)
День 10: На production неправильная схема
Эта колонка не индексирована, поэтому медленно
Кто это создал? Когда? Почему?
Что такое Migration
Migration — это файл с SQL/код, который описывает изменение схемы БД.
-- migrations/001_create_users_table.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- migrations/002_add_phone_to_users.sql
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
-- migrations/003_add_email_index.sql
CREATE INDEX idx_users_email ON users(email);
Плюсы:
- История всех изменений (git)
- Воспроизводимость (новый разработчик → одна команда)
- Revert (откатить изменение)
- Контроль версий (кто изменил когда)
Tools для миграций
1. Alembic (Python)
# alembic/versions/001_create_users.py
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table(
'users',
sa.Column('id', sa.UUID(), server_default=sa.text('gen_random_uuid()'), nullable=False),
sa.Column('email', sa.VARCHAR(255), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.func.now())
)
op.create_index('idx_users_email', 'users', ['email'])
def downgrade():
op.drop_index('idx_users_email')
op.drop_table('users')
2. Flyway (Java)
-- sql/V1__Create_users_table.sql
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL
);
-- sql/V2__Add_phone_column.sql
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
3. Liquibase (Universal)
<!-- db/changelog/001_create_users.xml -->
<changeSet id="1" author="john">
<createTable tableName="users">
<column name="id" type="UUID">
<constraints primaryKey="true"/>
</column>
<column name="email" type="VARCHAR(255)">
<constraints unique="true" nullable="false"/>
</column>
</createTable>
</changeSet>
4. Goose (Go, но работает везде)
# migrations/00001_create_users.up.sql
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL
);
# migrations/00001_create_users.down.sql
DROP TABLE users;
5. TypeORM/Sequelize (Node.js ORM)
// typeorm
import { MigrationInterface, QueryRunner, Table } from 'typeorm';
export class CreateUsers1234567890000 implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'users',
columns: [
{ name: 'id', type: 'uuid', isPrimary: true },
{ name: 'email', type: 'varchar', isUnique: true },
]
})
);
}
async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('users');
}
}
Типы миграций
1. Schema миграции (структура)
CREATE TABLE, ALTER TABLE, CREATE INDEX, DROP COLUMN
2. Data миграции (содержимое)
-- Например, добавить нового статуса
INSERT INTO statuses (name) VALUES ('archived');
-- Или изменить существующие данные
UPDATE users SET role = 'viewer' WHERE role IS NULL;
3. Destructive миграции (опасно!)
DROP TABLE, DROP COLUMN, DELETE FROM
-- Требуют backup и extra caution
Lifecycle миграции
Разработчик пишет код
↓
Создаёт migration файл
↓
Выполняет: npm run migrate:up (или equivalent)
↓
База обновляется
↓
Разработчик тестирует код
↓
Коммитит в git (код + migration)
↓
Pull request
↓
Deploy в staging
↓
Миграция автоматически выполняется в staging
↓
Проверяем в staging
↓
Deploy в production
↓
Миграция автоматически выполняется в production
Практический пример (Node.js + Goose)
# Инициализировать миграции
goose init -dir migrations
# Создать новую миграцию
goose create add_email_index
# Создаст: migrations/NNNN_add_email_index.up.sql
# migrations/NNNN_add_email_index.down.sql
-- migrations/0002_add_email_index.up.sql
CREATE INDEX idx_users_email ON users(email);
-- migrations/0002_add_email_index.down.sql
DROP INDEX idx_users_email;
# Выполнить миграции
goose up
# Статус
goose status
# Откатить последнюю
goose down
Beste practices
1. Одна миграция = одно изменение
-- ПЛОХО - слишком много
CREATE TABLE users (...);
CREATE TABLE posts (...);
CREATE TABLE comments (...);
CREATE INDEX idx_users_email ON users(email);
ALTER TABLE users ADD COLUMN verified BOOLEAN;
-- ХОРОШО - разделить
-- 0001_create_users.sql
CREATE TABLE users (...);
-- 0002_create_posts.sql
CREATE TABLE posts (...);
-- 0003_create_comments.sql
CREATE TABLE comments (...);
-- 0004_add_email_index.sql
CREATE INDEX idx_users_email ON users(email);
-- 0005_add_verified_column.sql
ALTER TABLE users ADD COLUMN verified BOOLEAN;
Почему? Легче откатить, понять, отладить.
2. Миграции идемпотентны (безопасно выполнять несколько раз)
-- ПЛОХО - упадет если индекс уже существует
CREATE INDEX idx_users_email ON users(email);
-- ХОРОШО - IF NOT EXISTS
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
3. Всегда пишите DOWN (откат)
-- UP: Добавить колонку
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
-- DOWN: Удалить колонку
ALTER TABLE users DROP COLUMN phone;
Почему? На production может понадобиться откатить.
4. Тестируй миграции локально
# Примени миграцию
make migrate-up
# Проверь что работает
npm test
# Откати и проверь что работает down
make migrate-down
make migrate-up # Заново, чтоб убедиться идемпотентно
# Всё ок, коммитим
git commit -m "feat: add phone column to users"
5. Большие изменения разбей на несколько миграций
-- Вместо одной большой миграции:
-- 1. Добавить новую колонку (с NULL)
ALTER TABLE users ADD COLUMN new_column VARCHAR(100);
-- 2. Заполнить данные (может быть долго)
UPDATE users SET new_column = computed_value();
-- 3. Сделать NOT NULL
ALTER TABLE users ALTER COLUMN new_column SET NOT NULL;
-- Это снижает риск блокировки таблицы
Проблемы и решения
Проблема 1: Миграция повисла (не завершилась)
# Проверим статус
psql -c "SELECT * FROM schema_migrations;"
# Откатим вручную если нужно
DELETE FROM schema_migrations WHERE version = '20240101_add_column';
Проблема 2: Конфликт миграций (два разработчика создали параллельно)
0001_add_user_column.sql (Разработчик A)
0001_add_product_column.sql (Разработчик B)
Решение: Переименовать один на 0002
Проблема 3: Миграция влияет на performance
-- ПЛОХО - блокирует таблицу
ALTER TABLE users ADD COLUMN created_at TIMESTAMP;
-- ХОРОШО - в PostgreSQL не блокирует с CONCURRENTLY
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
-- Или разделить на несколько шагов
ALTER TABLE users ADD COLUMN created_at TIMESTAMP DEFAULT NOW();
-- Потом в отдельной миграции
ALTER TABLE users ALTER COLUMN created_at DROP DEFAULT;
Мой workflow
# 1. Создаю миграцию
goose create add_verified_column
# 2. Пишу SQL
# migrations/0005_add_verified_column.up.sql
ALTER TABLE users ADD COLUMN verified BOOLEAN DEFAULT FALSE;
# 3. Пишу откат
# migrations/0005_add_verified_column.down.sql
ALTER TABLE users DROP COLUMN verified;
# 4. Тестирую
make test
goose up
npm test
goose down
npm test
goose up
# 5. Коммитю
git add migrations/ src/
git commit -m "feat: add verified column to users"
# 6. На staging автоматически
# CI/CD запускает миграции
# 7. На production
# Миграции выполняются автоматически при deploy
Вывод: Миграции — это critical tool для production приложений. Используй их для версионирования схемы БД, тестируй локально, и коммитай с кодом.