Flyway
Q: Flyway 是什么?
Flyway 是独立于数据库的应用、管理并跟踪数据库变更的数据库版本管理工具。官方文档。
Q: Flyway 如何工作?
假设将Flyway应用在一个空的数据库中,它会去寻找metadata table(元数据表),如果找不到这张metadata table,Flyway会去创建一张叫SCHEMA_VERSION的空表作为metadata table。这张表将会被Flyway用来追踪记录数据库的状态。
Migration文件会按照他们的版本进行排序,每一个Migration文件被执行(应用),都会去更新metadata table。当再次扫描(执行)Migration文件时,会对metadata table进行检查,如果在metadata table里已经存在该Migration文件,则该Migration文件不会被执行。
所以需要改变当前数据库的表信息或者表结构的时候,只需要添加一个高于当前版本的Migration文件,当再次执行Flyway的时候,数据库就会被改变(升级)。
Q: Flyway 怎么用?
官网上介绍,Flyway的使用有六种方式,分别是Command-line、API、Maven、Gradle、Ant、SBT。
具体方式。
Q: Flyway 支持的数据库?
Oracle、SQL Server、SQL Azure、DB2、MySQL、MariaDB、PostgreSQL、Redshift、Vertica、H2、Hsql、Derby、SQLite、SAP HANA、solidDB、Sybase ASE、Phoenix等。
Q: Flyway 用在哪些框架里?
Spring Boot、Play、Dropwizard、Ninja Framework、Jooby Framework、flyway-test-extensions、Chef cookbook、Griffon Plugin、Grunt Plugin、Spring Roo Add-on等。
Q: Flyway 常用语句有哪些,各自的作用?
Flyway的主要语句有六个,分别是Migrate、Clean、Info、Validata、Baseline、Repair。
- migrate
主要有两个作用:一是migrate数据库到最新的版本,二是如果没有metadata table会自动创建。
- clean
会清空数据库中所有的对象,包括metadata table。一定不要用在生产环境。
- info
会打印所有Migration文件的详细信息和状态信息。这些信息可以让我们知道,哪些文件被执行,哪些没有被执行。
- validata
会帮助验证Migration文件与本地数据库是否匹配。
- baseline
基线是引入Flyway到现有数据库的一个特点的版本。它会忽略包括基线在内的所有数据库的版本,对于新的Migration文件也适用。
- repair
主要用来修复metadata table,包括两类问题:删除失败的Migration文件;调整和检验可用的Migration文件。
Migration
Q: Migration 产生的背景?
假设我们有一个项目叫Shiny,这个项目主要经营的是一个可持续交付的名叫Shiny Soft的软件,这个软件背后有一个数据库叫Shiny DB。
在多人合作的时候,这种模式就会变成各自机器上的环境、不断集成的环境、测试环境、生产环境等等一系列不同的环境,与之对应的也就有了不同的Shiny DB和Shiny Soft版本。
所以这个时候不仅要处理我们自己机器上的环境,同时也要解决其他环境出现的问题。如果是代码出现问题很容易解决,因为有版本管理工具、构建工具、持续集成、明确的发布和部署的过程。同样数据库也会出现很多和代码一样的问题,比如:这台机器上数据库的版本是什么?生产环境上数据库的版本是什么?怎么样在测试通过后应用在生产环境上?
Database migration 就会解决这些问题。它可以从头创建一个数据库,在任何时候都清楚数据库处于什么状态。
所以使用Migration对我们来说很方便。
Q: Migration 命名规则?
- 包含至少一个数字
- 由.或_分隔
- 开始的0是被忽略的
1 2 3 4 5 6
| 例子: 1.2.3.4.5.6.7.8.9 205.68 20130115113556 2013.1.15.11.35.56 2013.01.15.11.35.56
|
Flyway的使用方法
创建一个项目
使用Maven Archetype Plugin创建项目,语句如下:
1 2 3 4 5 6 7 8
| $ mvn archetype:generate -B \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.1 \ -DgroupId=foo \ -DartifactId=bar \ -Dversion=1.0-SNAPSHOT \ -Dpackage=foobar
|
然后进入到项目目录:$ cd bar
添加Flyway
在pom.xml里添加Flyway和H2的插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <project ...> ... <build> <plugins> <plugin> <groupId>org.flywaydb</groupId> <artifactId>flyway-maven-plugin</artifactId> <version>4.0.3</version> <configuration> <url>jdbc:h2:file:./target/foobar</url> <user>sa</user> </configuration> <dependencies> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.191</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
|
创建Migration文件
进入到Migration目录:src/main/resources/db/migration
,然后新建V1__Create_person_table.sql
文件,内容为SQL语句。
1 2 3 4
| create table PERSON ( ID int not null, NAME varchar(100) not null );
|
执行Migrate
在demo目录下创建数据库表,使用:$ mvn flyway:migrate
。会有以下提示信息:
1 2 3 4
| [INFO] Creating Metadata table: "PUBLIC"."schema_version" [INFO] Current version of schema "PUBLIC": << Empty Schema >> [INFO] Migrating schema "PUBLIC" to version 1 - Create person table [INFO] Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.062s).
|
创建第二个Migration文件
进入到Migration目录:src/main/resources/db/migration
,然后新建V2__Add_people.sql
文件,内容为SQL语句。
1 2 3
| 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');
|
执行$ mvn flyway:migrate
,命令行里提示以下信息表示成功。
1 2 3
| [INFO] Current version of schema "PUBLIC": 1 [INFO] Migrating schema "PUBLIC" to version 2 - Add people [INFO] Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.090s).
|
使用其他命令
1 2 3 4 5 6
| $ mvn flyway:clean $ mvn flyway:migrate $ mvn flyway:info $ mvn flyway:validata $ mvn flyway:baseline $ mvn flyway:repair
|
项目上出现的问题
问题背景
问题出在Develop环境向QA环境上传时,最开始QA与develop环境的代码是相同的,具体如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| **db.migration/文件夹** V1__Create_ProfitExcel_Table.sql V3__Create_Client_Catalogue_Table.sql V6__Create_Catalogues_Table.sql V7__Add_Catalogues.sql V8__Create_User_table.sql V9__Add_User.sql V10__Create_Authorities_Table.sql V11__Add_Authorities.sql V12__Create_Sub_Client_Table.sql V13__Create_Project_Manager_Table.sql **V1__Create_ProfitExcel_Table.sql的文件内容** create table ProfitExcel ( id serial PRIMARY KEY, profitYear INTEGER, profitMonth INTEGER, client varchar(40) not null, subProject varchar(40) not null, projectDescription varchar(140), netRevenue FLOAT , wages FLOAT , totalTravel FLOAT , otherCosts FLOAT , grossProfitMargin FLOAT , grossProfitMarginPercent varchar (100) ); CREATE INDEX profit_report_index ON ProfitExcel (profitYear, profitMonth, client, subProject);
|
因为要对V1__Create_ProfitExcel_Table.sql
创建的ProfitExcel表新增属性,所以增加了V14__Alter_Profit_Cloumn.sql
,具体内容如下:
1 2 3 4 5 6 7
| ALTER TABLE ProfitExcel ADD professional FLOAT; ALTER TABLE ProfitExcel ADD revenueprofessional FLOAT; ALTER TABLE ProfitExcel ADD revenuebillable FLOAT; ALTER TABLE ProfitExcel ADD revenuebaddebt FLOAT; ALTER TABLE ProfitExcel ADD costtravelbillable FLOAT; ALTER TABLE ProfitExcel ADD costtraveltravel FLOAT; ALTER TABLE ProfitExcel ADD costtotalcosts FLOAT;
|
在本地环境里migrate完全没有任何问题,执行git add & git commit
后,诡异的事情出现了,QA环境编译报错了。报错如下:
1 2
| 找不到报错信息了= =。 大概就是不存在V1的ProfitExcel表。
|
解决过程
- 认为
V1__Create_ProfitExcel_Table.sql
的名字写错了,更改为V1__Create_Profit_Table.sql
之类的。
当然不是这个原因。就在这个时候又出现了一个问题:Ci提示QA环境有两个V1文件。
这就奇怪了,在本地执行时我会先执行mvn flyway:clean
保证本地的数据库是空的,再去执行mvn flyway:migrate
,在QA环境是不需要我去执行clean的命令的,因为每一次跑代码都是一个新的容器。
先去检查了QA环境上CI生成的war包,发现war包里只有一个V1的文件。所以问题就定位在了docker里,经过好长时间的检查,发现是因为JenkinsFile的配置文件里,在打包的时候没有清空之前的class文件。
解决方式:
1 2 3 4 5 6 7 8 9 10 11
| 修改前: stage 'BUILD' docker.image('maven:3.3.3-jdk-8').inside('-v/root/.m2:/root/.m2') { sh 'mvn package' } 修改后: stage 'BUILD' docker.image('maven:3.3.3-jdk-8').inside('-v/root/.m2:/root/.m2') { sh 'mvn clean package' }
|
- 推测V1没有执行,可以把V1的内容写在V2里。
可是并没有起作用,又把V1的内容复制在V5里,依旧没有用。
…
…
经过很多次的试验之后,发现在配置文件里有flyway.baselineVersion=0
是被注释掉的,执行baseline之后也没有什么卵用。
又把配置文件的flyway.baselineVersion=0
改为flyway.baselineVersion=1
,发现报错信息变了。提示已经存在ProfitExcel表,不能重复创建。到这里问题就解决了!WoW~
解决方法
按照道理来说QA环境每一次编译War包都是会新的database(空空的database,一张表也没有),但奇怪的是QA环境每一次都会有一张表存在在database里,这就是导致develop和QA环境不同的最根本的原因。设置Flyway的Baseline为1,将V1__Create_ProfitExcel_Table.sql
里的文件写在V2__Create_ProfitExcel_Table.sql
里,这样就能保证QA环境与Develop环境初始化时候的database是相同的。