Миграции баз данных в 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