Миграции и контроль версий БД с помощью Flyway
Feb 21, 2020 07:57 · 847 words · 4 minute read
Процесс преобразования одной структуры базы данных в другой без потери косистентности, при котором меняется схема (таблицы и их колонки, индексы и пр.) называется миграцией. В данной статье рассмотрим использование инструмента flyway
для работы с миграциями в БД postgres - давайте разберемся!
Можно ли обойтись без миграций? Да, если это:
- совсем маленький pet-проект;
- stateless приложение;
- данные не важны и могут быть легко удалены/восстановлены;
- приложение не развивается и/или не меняет структуру хранения данных.
В остальных случаях стоит использовать миграции - хотя бы исходя из правил “хорошего тона”.
В терминогии flyway
любые изменения в базе данных называются миграциями. Миграции могут быть версионированными (versioned) и повторяемыми (repeatable).
Версионированные миграции имеют версию (очевидно же), описание и контрольную сумму. Версия должна быть уникальна, а описание достаточно информативным - при виде описания вы должны понимать, для чего предназначена эта миграция на самом деле. Контрольная сумма используется для обнаружения (и блокирования) случайных изменений. Версионнированные миграции - это наиболее частый тип миграций (их мы и будем подробно рассматривать), которые применяются к БД единожды.
Повторяемые миграции имеют описание и контрольную сумму, но у них нет версии. Таким образом, вместо того, чтобы применяться единожды, эти миграции будут переприменяться каждый раз, когда меняется их контрольная сумма. В процессе применения миграций, повторяемые миграции будут выполняться ПОСЛЕ всех версионированных и в алфавитном порядке их описания.
По умолчанию, миграции могут быть написаны на Java или SQL (наш случай) и содержать в себе несколько утверждений. Для контроля работы с миграциями (какие миграции уже применены, кем и когда), утилита flyway
добавляет в базу данных таблицу flyway_schema_history
.
Пример версионированной миграции:
CREATE TABLE hero_data.hero
(
id BIGSERIAL NOT NULL,
name VARCHAR(250) NOT NULL,
description TEXT NOT NULL,
debut_year INT NOT NULL,
appearances INT NOT NULL,
special_powers INT NOT NULL,
cunning INT NOT NULL,
strength INT NOT NULL,
technology INT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
ALTER TABLE hero_data.hero ADD CONSTRAINT pk_hero_id PRIMARY KEY (id);
Пример повторяемой миграции:
CREATE OR REPLACE VIEW blue_cars AS
SELECT id, license_plate FROM cars WHERE color='blue';
Как правило, миграции проще всего писать в формате SQL - так легче начать работу, использовать существующие скрипты/навыки/инструменты. Опять же, такой подход сразу дает доступ ко всем возможностям используемой базы данных и не требует понимания “промежуточных” уровней транслирования sql-выражений. SQL-миграции хорошо подходят для:
- DDL-изменений (CREATE/ALTER/DROP утверждения для TABLES,VIEWS,TRIGGERS,SEQUENCES,…);
- Несложные изменения справочных данных (CRUD in reference data tables);
- Несложные и массовые изменения данных (CRUD in regular data tables).
SQL-миграции должны соответствовать определенному шаблону именования для того, чтобы flyway
мог работать с ними. Имя файла должно содержать:
- Префикс: V для версионированных и R для повторяемых миграций (можно изменить);
- Версию: Версия может содержать точки и/или нижние подчеркивания (только не для повторяемых миграций). Я бы рекомендовал использовать для версии timestamp в формате 20200214141830. Чтобы получить отформатированный timestamp выполните команду
date '+%Y%m%d%H%M%S'
; - Разделитель: __ (два нижних подчеркивания, можно изменить);
- Описание: слова разделяются пробелами или нижними подчеркиваниями;
- Суффикс: .sql (настраивается).
Пример правильно названного файла с миграциями - V20200214141830__Create_hero_schema.sql
.
Миграции могут быть применены ко всем необходимым базам данных, но для простоты рассмотрим пример работы только с одной БД.
Все взаимодействие утилиты flyway
с БД будет осуществляться из docker-контейнера, дабы не захламлять систему. Для сборки образа воспользуемся следующим Dockerfile
:
FROM adoptopenjdk/openjdk13:jdk-13.0.2_8-ubuntu-slim
LABEL maintainer="Yevhen Lebid <ealebed@gmail.com>"
WORKDIR /flyway
ARG FLYWAY_VERSION
ENV FLYWAY_VERSION=${FLYWAY_VERSION}
RUN apt-get update && \
apt-get install -y \
curl && \
apt-get -y autoremove && \
apt-get -y clean && \
rm -rf /var/lib/apt/lists/*
RUN curl -L https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/${FLYWAY_VERSION}/flyway-commandline-${FLYWAY_VERSION}.tar.gz -o flyway-commandline-${FLYWAY_VERSION}.tar.gz \
&& tar -xzf flyway-commandline-${FLYWAY_VERSION}.tar.gz --strip-components=1 \
&& rm flyway-commandline-${FLYWAY_VERSION}.tar.gz \
&& ln -s /flyway/flyway /usr/local/bin/flyway
Соберем образ flyway
следующей командой:
docker build \
-f Dockerfile \
--build-arg FLYWAY_VERSION=6.2.4 \
-t ealebed/flyway:1.0.0 .
Предположим, что у нас уже существует БД. Чтобы начать с ней работать используя миграции, необходимо:
- создать sql-скрипт, содержащий текущее состоянии БД (DDL, Data Definition Language) со всеми индексами, триггерами, процедурами и т.д. (например, используя
pg_dump -s ...
); - создать версионированную миграцию из данного скрипта, используя корректную версию и описание (например, V20190814141830__Base_version.sql);
- назначить (зафиксировать) базе данных базовую версию, используя версию и описание с предыдущего шага. Чтобы выполнить данную операцию в docker-контейнере запустите:
docker run \
--rm
-e FLYWAY_USER=${FLYWAY_USER} \
-e FLYWAY_PASSWORD=${FLYWAY_PASSWORD} \
-e FLYWAY_URL=${FLYWAY_URL} \
ealebed/flyway:1.0.0 flyway baseline -baselineVersion=20190814141830 -baselineDescription="Base version"
Здесь ${FLYWAY_USER}
, ${FLYWAY_PASSWORD}
и ${FLYWAY_URL}
- необходимые параметры для авторизованного подключения к интересующей вас БД. Полный список параметров можно найти здесь.
Теперь можно приступать к написанию миграций. Если вы работаете в IntelliJ IDEA, то рекомендую использовать плагин Flyway Migration Creation.
Для того, чтобы применить одну или несколько миграций (помним о версионировании), необходимо смонтировать внутрь docker-контейнера каталог с sql-миграциями и выполнить команду migrate
:
docker run \
--rm \
-v <path_to_database_directory>:/flyway/sql \
-e FLYWAY_USER=${FLYWAY_USER} \
-e FLYWAY_PASSWORD=${FLYWAY_PASSWORD} \
-e FLYWAY_URL=${FLYWAY_URL} \
ealebed/flyway:1.0.0 flyway migrate
Просмотреть детальную информацию и статус всех миграций можно с помощью команды info
:
docker run \
--rm \
-e FLYWAY_USER=${FLYWAY_USER} \
-e FLYWAY_PASSWORD=${FLYWAY_PASSWORD} \
-e FLYWAY_URL=${FLYWAY_URL} \
ealebed/flyway:1.0.0 flyway info
Стоит отметить, что в платной версии flyway
есть возможность выполнять проверку применения миграций с помощью так называемых Dry Runs - вещь очень полезная (иногда и жизненно необходимая). В одной из следующих статей я расскажу как можно организовать аналогичные проверки и валидацию миграций еще до их применения к БД, причем совершенно бесплатно, без смс и регистрации!