Flyway 是一个用Java编写的开源数据库版本管理工具,或者说是数据库结构变更工具,旨在帮助开发和运维更容易地管理数据库演进过程中的各个版本。它的源代码在 github 上。
简介
在开发过程中,数据库是不断向前演进的,可以说是拥有“版本”这个概念。通常当在生产环境部署新代码的时候,会由开发或者DBA来做数据库结构变更的操作。当数据库较小,环境数量少的时候,人工操作比较有把握,直接就人肉来做结构变更了。可要是在数据库复杂、环境数量多(开发、测试、预发布、生产……)的情况下,人工处理这样的事就开始有些令人担心了。如何保证所有环境的数据库结构是一致的?如何知道当前环境的数据库是哪个状态?如何知道生产环境的一个关于数据库的hotfix是否也在预发布环境中执行了?Flyway就是用来解决这样的问题的工具。它的原理非常简单,就是在数据库中创建一张自己用的表,例如schema_version,在里面存放数据库当前的状态,以此来管理数据库的版本。Flyway提供了 命令行 、 API 、 Maven 、 Gradle 、 Ant 、 SBT 等各种方式,来让我们更容易将其与自己的项目结合。类似的工具还有 Liquibase 、 dbdeploy 等。
本文将会用Docker来创建一个mysql的实例,用Maven来创建一个包含数据库的Java项目,并使用Flyway来进行版本管理。
准备环境
首先请自行安装Docker、Java、Maven和喜欢的IDE。我用的是 Intellij IDEA社区版。我用Vagrant启动了一台已经安装过Docker的Ubuntu虚拟机,它的IP是 192.168.33.88
,可以用以下命令来直接启动mysql容器来提供数据库服务并创建ggg数据库:
docker run -d --net=host --name=mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.12
docker exec -it mysql mysql -uroot -p123456 -e 'create database ggg;'
接下来新建一个Maven工程helloFlyway:
mvn archetype:generate -B \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.1 \
-DgroupId=org.ggg.flyway \
-DartifactId=helloFlyway \
-Dversion=1.0-SNAPSHOT \
-Dpackage=org.ggg.flyway
在 helloFlyway/pom.xml
里加入flyway的maven插件和mysql-connector-java的依赖(别忘了根据你自己的数据库来配置jdbc):
<project ...>
...
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>4.0.3</version>
<configuration>
<url>jdbc:mysql://192.168.33.88:3306/ggg</url>
<user>root</user>
<password>123456</password>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
接下来创建一个数据库版本1的文件:
cd helloFlyway
mkdir -p src/main/resources/db/migration/
cat << EOF > src/main/resources/db/migration/V1__Create_person_table.sql
create table PERSON (
ID int not null,
NAME varchar(100) not null
);
EOF
工程创建好并用intellij导入后,应该是这样的:
![](http://img1.tuicool.com/3Inm2mj.jpg!web)
版本管理
使用以下命令来运行flyway:
mvn flyway:migrate
出现 BUILD SUCCESS 就说明数据库结构变更已经完成了。我们可以登录到数据库看一看:
docker exec -it mysql mysql -uroot -p123456
运行以下sql就可以看到flyway确实已经起作用了:
USE ggg
SHOW TABLES;
SELECT * FROM schema_version;
而表schema_version里面也有了一条记录。接下来再创建一个数据库版本2的文件:
cat << EOF > src/main/resources/db/migration/V2__Add_people.sql
insert into PERSON (ID, NAME) values (1, 'Axel');
insert into PERSON (ID, NAME) values (2, 'Mr. Foo');
insert into PERSON (ID, NAME) values (3, 'Ms. Bar');
EOF
再次用同一条命令来运行flyway:
mvn flyway:migrate
就会看到日志里显示 Current version of schema ggg: 1 和 Migrating schema ggg to version 2 - Add people 。如果我们重复运行 mvn flyway:migrate
,就会看到 Schema ggg is up to date. No migration necessary 。现在查看一下两张表:
SELECT * FROM PERSON;
SELECT * FROM schema_version;
PERSON表里已经有了 V2__Add_people.sql
里的三条记录,而schema_version表里,就可以看到现在的版本为2。通过这样的方式,就可以管理数据库的版本了。
历史数据库
如果数据库里已经有历史数据了,那就会稍微麻烦一点儿。我们来试试看,首先改造一下ggg数据库:
RENAME TABLE PERSON TO STUDENT;
DROP TABLE schema_version;
这样一来,数据库的版本便被清空,PERSON表也变成了STUDENT表。如果直接运行 mvn flyway:migrate
会报错: org.flywaydb.core.api.FlywayException: Found non-empty schema ggg without metadata table 。这时候需要用到另一个命令:
mvn flyway:baseline
上面的 baseline
命令会以现在的数据库结构为基础,创建一张schema_version表,标明现在的版本是1。接着运行 mvn flyway:migrate
还会报错: Migration of schema ggg
to version 2 - Add people failed 。这是因为数据库现在是版本1,所以会忽略 V1__Create_person_table.sql
而直接执行 V2__Add_people.sql
,而 V2__Add_people.sql
依赖于 V1__Create_person_table.sql
里创建的PERSON表,所以失败了。解决的方法也很简单,改变两个sql文件的版本即可:
mv src/main/resources/db/migration/V1__Create_person_table.sql src/main/resources/db/migration/V1_1__Create_person_table.sql
mv src/main/resources/db/migration/V2__Add_people.sql src/main/resources/db/migration/V1_2__Add_people.sql
mvn flyway:migrate
Flyway的命名规范如下:以 V
开头, .sql
结尾,版本号可以使用 .
或者 _
,版本号和描述之间用两个下划线 __
分开。要是执行成功,但是却看不到PERSON表,那很可能是因为schema_version表里的版本已经是一个 错误的 版本2了,运行repair之后再重新migrate即可:
mvn flyway:repair
mvn flyway:migrate