goose для ClickHouse
↑ 5.1 Миграции: EF Core, Flyway, goose · ← Предыдущая · → Следующая
Зачем это на собесе
Для аналитического стека нужна своя дисциплина миграций: ClickHouse не транзакционен, и DDL ведёт себя иначе.
goose
Лёгкий инструмент миграций на Go: SQL-файлы (и Go-миграции), поддержка PostgreSQL, MySQL, SQLite, ClickHouse и др. История хранится в таблице goose_db_version.
goose -dir migrations clickhouse "tcp://default:pass@localhost:9000/analytics" up
goose -dir migrations clickhouse "..." status
goose -dir migrations clickhouse "..." down
goose create add_events_table sqlФормат файла:
-- migrations/20260930120000_add_events_table.sql
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS events
(
ts DateTime,
tenant_id UInt32,
event_type LowCardinality(String),
payload String
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(ts)
ORDER BY (tenant_id, ts);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS events;
-- +goose StatementEndStatementBegin/End — для многострочных операторов.
Особенности ClickHouse
- нет транзакций для DDL: миграция из нескольких операторов при ошибке остаётся применённой частично. Делайте одну миграцию = один оператор и пишите идемпотентные скрипты (
IF NOT EXISTS,IF EXISTS); ALTERдля MergeTree: добавление и удаление столбцов быстрые (метаданные), изменение типа иMODIFY ORDER BYограничено (ключ сортировки нельзя просто переписать: создаётся новая таблица и данные переносятся);- ключ сортировки, партиционирование и движок неизменяемы: изменение = новая таблица +
INSERT SELECT+EXCHANGE TABLES/RENAME; - кластер:
ON CLUSTERдля DDL на всех узлах; для реплицируемых таблиц через Keeper; в goose нужно указыватьON CLUSTERявно в SQL; - материализованные представления: при изменении схемы источника нужно обновить MV (
ALTER TABLE ... MODIFY QUERYв новых версиях или пересоздание); - бэкфилл исторических данных делается отдельным
INSERT INTO ... SELECT(может быть долгим и идти в фоне); - в таблицу истории goose для кластера лучше указать реплицируемый движок, либо запускать миграции на одном узле через Distributed-DDL.
Паттерн: смена ключа сортировки
-- 1. новая таблица
CREATE TABLE events_v2 AS events ENGINE = MergeTree ORDER BY (tenant_id, event_type, ts);
-- 2. перенос (можно по партициям)
INSERT INTO events_v2 SELECT * FROM events;
-- 3. атомарная подмена
EXCHANGE TABLES events AND events_v2;
-- 4. позднее удалить events_v2 (старая версия)Паттерн: добавление столбца
ALTER TABLE events ADD COLUMN IF NOT EXISTS browser LowCardinality(String) DEFAULT '' AFTER event_type;Быстро (старые парты вычисляют значение по умолчанию на лету).
Запуск в CI/CD и Docker
docker run --rm --network app -v "$PWD/migrations:/migrations:ro" ghcr.io/pressly/goose:latest \
-dir /migrations clickhouse "tcp://migrator:${CH_PASSWORD}@clickhouse:9000/analytics" upИли встроенный режим на Go (библиотека goose.Up(db, dir)), подходит для сервисов на Go; для .NET — контейнер goose отдельным шагом.
Альтернативы
clickhouse-migrations (Node), golang-migrate, Atlas (declarative для ClickHouse), dbmate, Liquibase (плагин ClickHouse), EF Core не поддерживает ClickHouse.
Практики
- версионируйте и DDL таблиц, и MV, и словари;
- тестируйте миграции на копии схемы в контейнере ClickHouse (Testcontainers);
- учитывайте неблокирующую природу: изменения применяются во время работы запросов;
- отдельный пользователь
migratorс правами DDL, а приложения только наINSERT/SELECT; - для
downпомните:DROP TABLEуничтожает данные, используйте осторожно и запрещайте на проде.
Вопросы с ответами
Почему в ClickHouse советуют одну миграцию на один оператор?
DDL не транзакционен: при ошибке на втором операторе первый уже применён. Мелкие идемпотентные миграции проще повторить и диагностировать.
Как изменить ключ сортировки таблицы MergeTree?
Создать новую таблицу с нужным
ORDER BY, перелить данныеINSERT SELECT, подменить черезEXCHANGE TABLESи удалить старую.
Как применять миграции в кластере ClickHouse?
Использовать
ON CLUSTERв DDL (через Keeper) и запускать goose от одного клиента; следить за консистентностью на всех репликах.