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

Что такое 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 приложений. Используй их для версионирования схемы БД, тестируй локально, и коммитай с кодом.