
Flyway介紹
Flyway的定位:數(shù)據(jù)庫的版本控制。
用一種簡單、干凈的方案,幫助用戶完成數(shù)據(jù)庫遷移的工作。使用Flyway,用戶可以從任意一個數(shù)據(jù)庫版本遷移到最新版本,簡單而且有效。
支持多個平臺:windows、ios、linux、docker、java、android
2018年度,產(chǎn)品被下載1千萬多次:

開源:

支持多種使用方式:

基于命令行模式,用戶從官網(wǎng)下載工具包,進行一些必要的配置,就可以通過命令行使用其功能。
基于Java API,用戶可以將Flyway提供的第三方包加入classpath,通過Flyway提供的API來使用其功能。
基于Maven或Gradle,用戶可以通過配置插件,運行mvn或gradle命令來使用其功能。
支持多種數(shù)據(jù)庫:

被spring-boot集成:

官方文檔簡潔,以我這樣蹩腳的英語水平,也很容易理解。
P.S. 開源版本支持大部分常用的功能,F(xiàn)lyway還有商業(yè)版本,會支持一些額外的功能。
Flyway的工作模式
這一節(jié)主要介紹Flyway是如何工作的,也可以理解為flyway的數(shù)據(jù)庫升級方案。
Flyway可以對數(shù)據(jù)庫進行升級,從任意一個版本升級到最新的版本。但是升級的依據(jù)是用戶自己編寫的sql腳本,用戶自己決定每一個版本的升級內(nèi)容。
Flyway不限定腳本里面的內(nèi)容,但是對腳本文件的名稱有一定的要求:

版本號可以使用小版本,如V1.1。
具體要求:
- 版本號和版本描述之間,使用兩個下劃線分隔。
- 版本描述之間,使用一個下劃線分隔單詞。
- 版本號唯一:不允許多個腳本文件有相同的版本號。
使用Flyway升級,flyway會自動創(chuàng)建一張歷史記錄表:flyway_schema_history。
這張表記錄了每一次升級的記錄,包括已經(jīng)執(zhí)行了哪些腳本,腳本的文件名,內(nèi)容校驗和,執(zhí)行的時間和結(jié)果:

flyway在升級數(shù)據(jù)庫的時候,會檢查已經(jīng)執(zhí)行過的版本對應(yīng)的腳本是否發(fā)生變化,包括腳本文件名,以及腳本內(nèi)容。如果flyway檢測到發(fā)生了變化,則拋出錯誤,并終止升級。
如果已經(jīng)執(zhí)行過的腳本沒有發(fā)生變化,flyway會跳過這些腳本,依次執(zhí)行后續(xù)版本的腳本,并在記錄表中插入對應(yīng)的升級記錄。
所以,flyway總是冪等的,而且可以支持跨版本的升級。
如果你好奇,flyway如何檢查腳本文件的內(nèi)容是否有修改。你可以注意以下記錄表中有一個字段checksum,它記錄了腳本文件的校驗和。flyway通過比對文件的校驗和來檢測文件的內(nèi)容是否變更。
使用上面的方式,升級一個空的數(shù)據(jù)庫,或者在一直使用flyway升級方案的數(shù)據(jù)庫上進行升級,都不會又問題。但是,如果在已有的數(shù)據(jù)庫引入flyway,就需要一些額外的工作。
flyway檢測數(shù)據(jù)庫中是否有歷史記錄表,沒有則代表是第一次升級。此時,flyway要求數(shù)據(jù)庫是空的,并拒絕對數(shù)據(jù)庫進行升級。
你可以設(shè)置baseline-on-migrate參數(shù)為true,flyway會自動將當(dāng)前的數(shù)據(jù)庫記錄為V1版本,然后執(zhí)行升級腳本。這也表示用戶所準(zhǔn)備的腳本中,V1版本的腳本會被跳過,只有V1之后的版本才會被執(zhí)行。
下文在介紹Maven客戶端的時候,會介紹另一種方案,實現(xiàn)在已有數(shù)據(jù)庫中第一次引入flyway。
Flyway的使用場景
命令行
用戶可以在官網(wǎng)下載適合自己平臺的工具包,進行相關(guān)配置之后,就可以通過命令行的方式使用Flyway。
這一塊只是在官網(wǎng)上看到其介紹,本人并沒有嘗試,我直接選擇了后面的方案。
使用Maven或Gradle插件
這種方式可以代替命令行的方式,因為我們項目中就使用maven,所以我更傾向于使用這種方式。
以Maven為例,在pom文件中進行必要的配置,包括插件及插件所需要的一些數(shù)據(jù)庫連接信息,就可以通過運行插件來使用其功能。
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
<properties>
<flyway.user>postgres</flyway.user>
<flyway.password>postgres</flyway.password>
<flyway.url>jdbc:postgresql://localhost:5432/test?currentSchema=demo_flyway</flyway.url>
<flyway.driver>org.postgresql.Driver</flyway.driver>
</properties>
<dependencies>
...
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
...
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
maven插件所支持的命令:

使用maven命令執(zhí)行插件,默認在classpath:/db/migration目錄搜索腳本,如果該目錄不存在,命令將被忽略。
所有的命令,以如下的格式執(zhí)行:
mvn flyway:{flyway-command}
migrate
mvn flyway:migrate
這個命令會搜索默認的腳本目錄,檢測并根據(jù)結(jié)果選擇執(zhí)行升級腳本。
clean
mvn flyway:clean
這個命令會清除指定schema下所有的對象,包括table、view、triggers...,讓schema變成空的狀態(tài)。
info
mvn flyway:info
這個命令顯示指定schema的升級狀態(tài),當(dāng)前的數(shù)據(jù)庫的版本信息。
validate
mvn flyway:validate
這個命令用于校驗,范圍包括已升級的腳本是否改名,已升級的腳本內(nèi)容是否修改。所有針對已升級的腳本進行的改動都會導(dǎo)致校驗失敗。
執(zhí)行migrate會自動進行校驗,如果失敗將不會做任何的migrate。
flyway希望用戶提供的腳本是穩(wěn)定的,以免造成額外的復(fù)雜性和混亂。
baseline
mvn flyway:baseline
如果用戶從一個已有的數(shù)據(jù)庫導(dǎo)出腳本,作為flyway的升級腳本。已存在的數(shù)據(jù)庫是不需要升級的。
baseline用于將當(dāng)前數(shù)據(jù)庫標(biāo)記為baseline,并記錄version為1。這表示用戶繼續(xù)執(zhí)行migrate命令時,會自動跳過V1版本對應(yīng)的腳本。
而對于空的數(shù)據(jù)庫,因為沒有執(zhí)行baseline,所以可以正常的執(zhí)行V1版本對應(yīng)的腳本。
P.S. 手動修改flyway自動生成的baseline記錄,將版本號改為其他的版本號,將自動跳過該版本及更早的版本。
Java API
Flyway提供了基于Java的API包,用戶可以將API包引入maven依賴,直接通過調(diào)用其API來執(zhí)行相關(guān)命令。
Spring-Boot集成了Flyway,只要把API包加入classpath,spring-boot在啟動應(yīng)用時會去指定的目錄查找腳本文件,并根據(jù)一定的策略選擇或忽略執(zhí)行。
使用Spring-Boot,用戶不必再顯式的編寫代碼調(diào)用API,只需要將腳本文件放在約定的目錄,或者告訴Spring-Boot你把腳本文件放在哪里了。
如果用戶需要實現(xiàn)非常靈活的遷移,Spring-Boot默認的方案無法滿足,也可以嘗試尋找自己編碼調(diào)用API的方案。
以下內(nèi)容介紹基于Spring-Boot + Maven的集成方案:
step1:在maven中引入flyway依賴
<?xml version="1.0" encoding="UTF-8"?>
<project ...>
...
<dependencies>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
...
</dependencies>
...
</project>
flyway-core即為我們所說的API包,除此之外,還要引入postgresql驅(qū)動包和spring-boot-starter-jdbc。
step2:配置application
按照常規(guī)的方式,在application.yml文件中配置spring.datasource系列:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/test?currentSchema=demo_flyway
driver-class-name: org.postgresql.Driver
username: postgres
password: postgres
spring為flyway準(zhǔn)備了專屬的數(shù)據(jù)源配置,但是在默認的情況下,可以直接使用spring.datasource的配置。

用戶可以將腳本放在約定的位置:classpath:/db/migration,或者配置一個自定義的位置:

step3:在指定的目錄編寫腳本
如果用戶沒有特地設(shè)置腳本的位置,則應(yīng)該在/db/migration創(chuàng)建腳本。否則,在對應(yīng)的位置創(chuàng)建腳本。
使用總結(jié)
我們是在中途嘗試使用flyway,所以開發(fā)環(huán)境會有一個已存在的數(shù)據(jù)庫。我們需要從開發(fā)環(huán)境中導(dǎo)出數(shù)據(jù)庫腳本,并對開發(fā)環(huán)境數(shù)據(jù)庫進行baseline標(biāo)記。導(dǎo)出的腳本可用于新環(huán)境的部署。
如果我們已經(jīng)有了生產(chǎn)環(huán)境,而且生產(chǎn)環(huán)境和開發(fā)環(huán)境的數(shù)據(jù)庫已經(jīng)有了較大的差異。暫時可以想到的方案大概有2個方案:
方案一:
在生產(chǎn)環(huán)境備份數(shù)據(jù)庫,然后創(chuàng)建一個全新的數(shù)據(jù)庫,手動將備份庫里的數(shù)據(jù)導(dǎo)入到新的數(shù)據(jù)庫。
方案二:
基于生產(chǎn)環(huán)境的數(shù)據(jù)庫,創(chuàng)建V1版本的腳本;基于開發(fā)庫相對于生產(chǎn)庫的變更,創(chuàng)建V2版本的腳本。在開發(fā)環(huán)境baseline,然后修改版本記錄,改為2。在生產(chǎn)環(huán)境中baseline,然后migrate使其升級到2。
方案一,需要更多的人工介入,但是比較穩(wěn)妥;方案二,難點在于溯源出正確的差異,編制V2腳本。