Миграции баз данных в Go: обзор инструментов и практик

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Миграции баз данных в Go (Golang): полное руководство

При разработке веб-приложений на Go рано или поздно возникает необходимость изменять структуру базы данных: добавлять новые таблицы, изменять существующие колонки или создавать индексы. Ручное выполнение SQL-скриптов в разных окружениях (разработка, стейджинг, продакшн) приводит к ошибкам и рассинхронизации. Решение этой проблемы — миграции баз данных.

В Go существует несколько зрелых библиотек для управления миграциями. В этой статье мы подробно разберём две самые популярные: golang-migrate/migrate и pressly/goose, а также приведём примеры их использования.

Что такое миграции и зачем они нужны?

Миграция — это версионированный набор SQL-команд (или Go-кода), который применяется к базе данных последовательно. Каждая миграция имеет уникальный идентификатор (обычно временная метка или номер) и может быть применена (up) или откачена (down).

Основные преимущества:

  • Автоматизация изменений схемы БД
  • Воспроизводимость — одинаковый результат на любой среде
  • Возможность отката изменений
  • Хранение истории изменений в коде (VCS-friendly)

1. Библиотека golang-migrate/migrate

Это самая популярная библиотека для миграций в Go. Поддерживает PostgreSQL, MySQL, SQLite, MongoDB и другие СУБД. Миграции могут быть написаны как на чистом SQL, так и на Go.

Установка

go get -u github.com/golang-migrate/migrate/v4

Структура файлов миграций

Файлы располагаются в папке migrations/ и именуются по шаблону:

{timestamp}_{title}.up.sql
{timestamp}_{title}.down.sql

Пример:

migrations/
  000001_create_users_table.up.sql
  000001_create_users_table.down.sql
  000002_add_email_to_users.up.sql
  000002_add_email_to_users.down.sql

Пример SQL-миграции

Файл 000001_create_users_table.up.sql:

CREATE TABLE IF NOT EXISTS users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

Файл 000001_create_users_table.down.sql:

DROP TABLE IF EXISTS users;

Применение миграций из Go-кода

package main

import (
    "github.com/golang-migrate/migrate/v4"
    _ "github.com/golang-migrate/migrate/v4/database/postgres"
    _ "github.com/golang-migrate/migrate/v4/source/file"
)

func main() {
    m, err := migrate.New(
        "file://migrations",
        "postgres://user:password@localhost:5432/dbname?sslmode=disable",
    )
    if err != nil {
        panic(err)
    }

    // Применить все доступные миграции (Up)
    if err := m.Up(); err != nil && err != migrate.ErrNoChange {
        panic(err)
    }

    // Откатить последнюю миграцию (Down)
    // if err := m.Steps(-1); err != nil {
    //     panic(err)
    // }
}

CLI-инструмент migrate

Также доступна утилита командной строки:

migrate create -ext sql -dir migrations -seq create_users_table
migrate -path migrations -database "postgres://..." up
migrate -path migrations -database "postgres://..." down 1

2. Библиотека pressly/goose

Goose — ещё один популярный инструмент, который изначально создавался как аналог ActiveRecord-миграций из Ruby on Rails. Поддерживает SQL и Go-миграции, а также встроенный CLI.

Установка

go get -u github.com/pressly/goose/v3

Структура файлов

Файлы именуются как {timestamp}_{title}.sql (без разделения на up/down — всё в одном файле через комментарии):

migrations/
  20240101120000_create_users_table.sql
  20240102150000_add_email_to_users.sql

Пример SQL-миграции для goose

-- +goose Up
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

-- +goose Down
DROP TABLE IF EXISTS users;

Применение миграций из Go-кода

package main

import (
    "database/sql"
    "github.com/pressly/goose/v3"
    _ "github.com/lib/pq                    

Рекомендаций