Flyway基礎簡介

1. 概述

Flyway是獨立於數據庫的應用、管理並跟蹤數據庫變更的數據庫版本管理工具。
自動升級(自動發現更新項):Flyway 會將任意版本的數據庫升級到最新版本。Flyway 可以脫離JVM 環境通過命令行執行,可以通過Ant 腳本執行,通過Maven 腳本執行(這樣就可以在集成環境自動執行),並且可以在應用中執行(比如在應用啓動時執行)。
規約優於配置:Flyway 有一套默認的規約,所以不需要修改任何配置就可以正常使用。
既支持SQL 腳本,又支持Java 代碼:可以使用SQL 腳本執行數據庫更新,也可以使用Java 代碼來進行一些高級數據升級操作。
高可靠性:在集羣環境下進行數據庫升級是安全可靠的。
支持清除已存在的庫表結構:Flyway 可以清除已存在的庫表結構,可以從零開始搭建您的庫表結構,並管理您的數據庫版本升級工作。
支持失敗修復。新的2.0 版本提供了repair 功能,用於解決數據庫更新操作失敗問題。

2. flyway基礎

2.1概述

  • flyway通過在數據庫中維護一張版本記錄表(默認名稱爲flyway_schema_history),對數據庫腳本操作進行版本管理,使得數據庫的升級更新可以自動化;
  • flyway執行的腳本支持兩種形式:一是使用SQL編寫的.sql腳本,二是把SQL操作寫到java代碼裏,通過執行java代碼來執行SQL;
  • 每個.sql或.class文件都是一個版本,文件名稱需要遵循flyway約定的格式;這個名稱中包含了一個數字的版本信息,flyway通過這個版本來進行管理;

2.2 flyway組成

  • flyway包由三部分組成
    • commandline: 對輸入的命令進行處理,確定配置文件路徑,查找依賴包等;
    • core: 實現flyway核心的操作(migrate/info/clean等)和版本管理
    • maven-plugin和gradle-plugin: 用於幫助打可運行的jar包;
  • flyway由java語言編寫,可以直接使用core來進行開發,而不需要使用commandline
    • 比如:自己建個springboot啓動工程(web或application),在裏面實例化Flyway對象,然後執行對應的操作
    • 比如:自己寫個main方法,在裏面實例化Flyway對象,然後執行對應的操作

2.3 flyway操作

命令 說明
migrate 執行最新版本的SQL腳本和java代碼,把結果反映到數據庫上
clean 清除數據庫中的所有表與數據,只剩下一個空的數據庫
info 列出用於維護版本信息的表內容,表名默認爲flyway_schema_history
validate 用實際的版本與數據庫中已有的版本進行對比,檢測意外修改(文件有checksum)
baseline 設定基線版本,對於已存在數據的數據,必須設置基線版本,後面從該版本之後的版本進行操作
repair 把版本表中失敗的記錄狀態變成pending,然後可以重新操作
undo 回滾對應版本,只有商用版本才支持

2.4 版本

  • Java文件
    在這裏插入圖片描述
  • SQL文件
    在這裏插入圖片描述
  • SQL文件與Java文件的不同之處僅在於後綴不同,一個是.sql(可配置),另外一個是.class(不可配置),其它是相同的;
  • 文件名(不含後綴)的格式
    • Prefix: 前綴,可以通過配置文件來設置
      • V: 代表正常版本管理;
      • U: 表示回滾,該操作比較危險,需要做詳盡測試才能用,實踐中最好不用;
      • R: 表示可以重複執行的版本,比如建視圖、存儲過程、函數等;
    • Version: 版本號,必須由數字(可加下劃線或點)組成,重複執行的腳本不需要版本;
    • Separator: 分隔版本與description的分隔符,爲兩個下劃線,可通過配置文件來修改,默認爲兩個下劃線;
    • Description: 文件內容描述,建議用下劃線分隔單詞;
    • Suffix: 只針對於sql文件(java文件不適用),可通過配置文件來設置,默認爲.sql;
  • Version
    • 一般情況下使用1~N的數字即可;
    • 考慮到實際開發中可能有多個分支進行操作,爲了使得分支合併不衝突,可使用時間戳來做版本(中間可用下劃線或點來分隔),時間戳越精確、衝突可能性越小;

2.5 路徑

  • INSTALLDIR: 安裝路徑,即flyway解壓所在的路徑,其用於:
    • 加載${INSTALLDIR}/drivers下的各種數據庫driver對應的jar包;
    • 加載${INSTALLDIR}/conf下的全局配置文件;
  • workingDirectory: 工作目錄,可通過指定命令參數“-workingDirectory=xxxx”(不含雙引號,xxx換成具體的絕對路徑),如果不指定則等同INSTALLDIR;
    • 加載${workingDirectory}/jars裏的jar包,通過java編寫的代碼需要打成jar放到該目錄下才能通過命令行的方式訪問到;
    • 加載${workingDirectory}/sql裏的SQL腳本文件,這個目錄會被配置文件flyway.conf裏的flyway.locations的值替換掉(注意是替換掉,不是並集,這個地方不好理解,原理是這兩個目錄都是賦值給location變量,配置文件裏有設置時會覆蓋掉);
    • 在flyway.conf裏的flyway.locations值,如果以filesystem:開頭、且是相對路徑的,都會基於此路徑;
  • 注意:該部分在flyway官網文檔中無說明(需要查看源碼才能知曉),也不好理解,容易出錯,需要好好理解(有比較多的地方並不遵循後面覆蓋前面的邏輯,所以不好理解)。這些路徑是在commandline中實現的,也就是說只有使用commandline纔用到

2.6 配置

2.6.1 配置加載

配置主要來源於flyway.conf配置文件,按以下順序和路徑進行加載配置:

  1. 先加載${INSTALLDIR}/conf/flyway.conf(flyway.conf文件名不可更改);
  2. 加載System.property中配置user.home對應的路徑下的flyway.conf文件(flyway.conf文件名不可更改),加載的配置若有相同則會覆蓋之前加載的;
    • System properties可以通過下面方式增加
      • java啓動時使用-D加的參數,如java -Dxxx=xxxx;
      • 在java代碼中使用System.setProperty(“xxx”, “xxxx”)添加的參數;
  3. 加載當前目錄下的flyway.conf文件(flyway.conf文件名不可更改),加載的配置若有相同則會覆蓋之前加載的;
    • 在命令行中,terminal或cmd進入的路徑爲當前路徑
    • 在java工程中運行則是工程根目錄
    • 該操作有點隱蔽,注意理解;
  4. 若在環境變量中配置了flyway.configFiles,會加載這些文件,加載的配置若有相同則會覆蓋之前加載的;
    • 該配置值可以有多個文件,用逗號分隔,每個文件可以有相對路徑(不能使用絕對路徑),文件名也不一定是flyway.conf;
    • 這些配置文件會基於workingDirectory{workingDirectory}來構成完成路徑,也就是這些文件需要放到{workingDirectory}目錄下;
  5. 若命令行參數中帶-configFiles參數指定了配置文件,則會加載這些文件,加載的配置若有相同則會覆蓋之前加載的;
    • 若環境變量中配置了flyway.configFiles,則此配置參數無效;
    • 該配置值可以有多個文件,用逗號分隔,每個文件可以有相對路徑(不能使用絕對路徑),文件名也不一定是flyway.conf;
    • 這些配置文件會基於workingDirectory{workingDirectory}來構成完成路徑,也就是這些文件需要放到{workingDirectory}目錄下;
  6. System.in輸入的配置(貌似用於測試,待研究);
  7. 用逗號分隔flyway.locations的值,如果裏面的路徑不是絕對路徑,則用${workingDirectory}補齊;
  8. 命令行還可以指定-flyway.xxxx=xxx形式的配置,這些配置會覆蓋上面已載的配置項
    • 注意:此處配置項,不是加載配置文件;

注意:

  • 該部分在flyway官網文檔中無說明(需要查看源碼才能知曉),也不好理解,因爲配置文件名大多是固定不可變的,而可以改變的手段有點特殊。
  • 要改變flyway.conf配置文件名稱,只有兩種方式;
  • 該部分主要屬於commandline範疇,直接使用core則可以自行構造配置項或自行指定配置文件並加載;
  • 配置裏的路徑可以使用變量(原理待研究);
  • 在命令行中指定-workingDirectory=xxxx時,若也指定-configFiles=flyway.conf,則會比較明顯地知道配置文件是在workingDirectory目錄下,但如果不指定configFiles則需要flyway.conf放到命令行進入的目錄中,會有點隱蔽;
  • 配置文件裏參數讀取後,{}格式的變量都會被替換,變量只能來源於環境變量,不在環境變量裏的變量會與{}整體被替換爲空(來源於命令行的參數沒有此處理過程,故不能使用${}格式的變量);

2.6.2 常用配置

# 配置sql和java code查找路徑:
#    sql文件路徑必須以filesystem:(含冒號)開頭,可以使用變量${workdir};
#    java code路徑需要以classpath:(含冒號)開頭,java代碼需要打成jar包,並放到工作目錄裏的jars目錄下,jar裏的目錄與該配置中classpath:後配置的路徑一致;
# 開發環境不要修改此值,它會自動找工程目錄下的db/sql中的sql腳本,以及代碼目錄com/everhomes/dbmigrate中的java代碼(兩種都會遞歸找子目錄)
flyway.locations=filesystem:${workdir}/sql,classpath:com/everhomes/dbmigrate

# 配置數據庫連接,注意在url中不要配置用戶名和密碼
flyway.url=jdbc:mysql://127.0.0.1:13306/blogs?characterEncoding=UTF-8&serverTimezone=UTC
flyway.user=root
flyway.password=123456

# 當需要低版本的腳本時,需要置成true,否則只能執行高版本的腳本
flyway.outOfOrder=true

# 當數據庫不是空數據庫時,需要配置爲true,空數據庫則可以不配置
# flyway.baselineOnMigrate=true
# 配置了該版本後,migrate會從比該版本大的版本進行migrate,不配置時默認值爲1
# flyway.baselineVersion=1

# 有數據的數據庫請保留此值爲true,禁用clean命令,執行clean命令會清空數據庫
flyway.cleanDisabled=true

# 記錄migrate歷史記錄的表名,默認爲 flyway_schema_history
flyway.table=schema_version_history

# migrate前後需要做某些操作時可使用此配置項,值爲類名(帶包路徑),多個類名之間使用逗號分隔
# 每個類需要實現org.flywaydb.core.api.callback.Callback接口
# flyway.callbacks=com.everhomes.dbmigrate.core.MigrateCallBack

2.7 版本管理

支持SQL-base和Java-base兩種管理方式,兩種方式產生的版本文件是共同管理的,即在命令行或Java代碼工程裏都有可以同時執行.sql和.class提供的版本管理,執行順序按版本號順序;

2.7.1 SQL-base migrate

基於SQL的版本管理:

  • 文件名稱需要符合版本格式
  • 文件可以被找到
    • SQL文件是通過flyway.locations配置項中指定以filesystem:開頭的路徑,可以有多個、用逗號分隔;
    • flyway.locations可以通過配置文件或命令行參數指定(參考上面“配置加載”章節說明);
    • 注意:flyway.locations值雖然可以通過多個途徑配置,但只能配置一個值(值中可逗號分隔),注意加載的順序和覆蓋情況;
  • 確定上面兩點後,即可執行flyway migrate/info/clean/repair 來進行版本管理;
    • 命令參數中可以按規範指定參數來幫助找到flyway.locations

2.7.2 Java-base migrate

基於Java的版本管理:

  • jar形式
    • 把開發的java代碼打包jar包,放到${workingDirectory}/jars中
      • java代碼須繼承org.flywaydb.core.api.migration.BaseJavaMigration(推薦)或實現org.flywaydb.core.api.migration.JavaMigration接口,使用此方式僅依賴flyway和jdk提供的jar即可,配置環境簡單;
      • java代碼儘量使用flyway和jdk提供的類和自定義的類來進行操作,否則要解決包依賴問題;
    • java代碼路徑可被找到(在jar包裏的路徑)
      • java代碼如果是放到db/migration路徑下(可有子目錄),則不用配置其它路徑即可訪問,因爲flyway裏默認給location加了個目錄: classpath:db/migration
      • 如果不是放到db/migration路徑下,則需要在flyway.locations裏配置以classpath:開頭的路徑,該路徑要與jar包裏的路徑一致;
  • java工程形式(在IDE裏運行java工程的形式)
    • 工程啓動運行有兩種方式
      • 引flyway的commandline包
        • 可指定flyway的安裝目錄,或者自行依賴數據庫driver包和flyway的core包;
        • 指定workingDirectory目錄;
        • 指定flyway.conf的目錄,或按規則放到commandline可找到的目錄;
        • 運行後執行commandline提供的org.flywaydb.commandline.Main.main(args)
        • 優點:可利用commandline已經實現的命令操作,按命令的方式操作;
        • 缺點:需要遵循commandline的方式;
      • 實例化flyway並運行
        • 按springboot的方式啓動或自行寫main方法啓動
        • 把flyway的core引入到工程中
        • 實例化flyway,並指定數據庫連接,然後調用migrate()/clean()等方法
        • 優點:靈活;
        • 缺點:需要對flyway各命令所依賴的配置項有比較多瞭解,纔好操作;
    • 該方式不受限於db/migration目錄,可隨意定目錄,但爲了部署時可以打包jar包,推薦遵循jar包所要求的目錄方式;
  • 注意:
    • 該部署在官方文檔說明不詳細,比較模糊,特別是jar的方式就沒有說明,部署時也只能使用jar方式;
    • 官方文檔主要說明了“實例化flyway”方式,但其背後依賴的配置項和規則也沒有說明;

2.7.3 callback

  • 版本管理操作有生命週期管理:整個操作開始前->單個版本操作開始前->單個statement操作開始前-> 具體操作 -> 單個statement操作後(報錯後)-> 單個版本操作後(錯誤後)-> 整個操作後(錯誤後)
    • 整個操作可能包含多個版本,每個版本里可能有多個sql statement
    • 操作是指migrate、clean、repair等,參考上面操作說明
  • 這些生命週期節點可以設置callback來進行一些額外的處理
    • 在java代碼中,需要實現org.flywaydb.core.api.callback.Callback接口,類路徑無規定
      • 接口要實現supports()方法,在裏面指定哪些操作歸該Callback進行處理
      • 實現handle()方法,具體處理業務
      • canHandleInTransaction()在有事務時才涉及;
    • 在flyway.callbacks配置中指定callback的類路徑(帶包名,多個類之間使用逗號分隔),flyway.callbacks可以在flyway.conf中指定或者在命令行參數中指定(參考上面配置加載章節說明);
    • callback的類包需要包含在jar中,才能在命令行裏操作;

2.7.4 不支持MySQL5.6

  • 在org.flywaydb.core.internal.database.mysql.MySQLDatabase.ensureSupported()中有對MySQL數據庫的版本進行判斷,小於5.7的版本不支持(MARIADB是MySQL的一個分支,都在MySQLDatabase中實現)。
  • 除此之外,沒有其它地方控制這個版本。拋出的錯誤說enterprise版本支持,但除非換代碼,否則感覺也不支持。
  • 要支持5.6,可以修改此類編譯與.class,然後替換掉core包裏的該類。
public final void ensureSupported() {
    ensureDatabaseIsRecentEnough("5.1");
    if (databaseType == DatabaseType.MARIADB) {
        ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("10.1", org.flywaydb.core.internal.license.Edition.ENTERPRISE);
        ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("10.2", org.flywaydb.core.internal.license.Edition.PRO);
        recommendFlywayUpgradeIfNecessary("10.4");
    } else {
        ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("5.7", org.flywaydb.core.internal.license.Edition.ENTERPRISE);
        if (JdbcUtils.getDriverName(jdbcMetaData).contains("MariaDB")) {
            LOG.warn("You are connected to a MySQL " + getVersion() + " database using the MariaDB driver." +
                    " This is known to cause issues." +
                    " An upgrade to Oracle's MySQL JDBC driver is highly recommended.");
        }
        recommendFlywayUpgradeIfNecessary("8.0");
    }
}

protected final void ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition(String oldestSupportedVersionInThisEdition, Edition editionWhereStillSupported) {
    if (!getVersion().isAtLeast(oldestSupportedVersionInThisEdition)) {
        throw new FlywayEditionUpgradeRequiredException(
                editionWhereStillSupported,
                databaseType,
                computeVersionDisplayName(getVersion()));
    }
}

2.7.5 版本比較

  • flyway把版本都封裝在org.flywaydb.core.api.MigrationVersion類裏
  • 比較的原理
    • 先把版本中的下劃線(_)替換成點(.),不支持其它非數字字符,有的話會報錯;
    • 把版本以點進行分隔成多個數字(點分隔的每段一個數字);
    • 把每一段數字轉成Bigdecimal,即版本的每段數字都可以很大,即使精確到毫秒的時間戳作爲版本也支持;
    • 兩個版本之間進行比較
      • 取兩個版本中段數量大的段數作爲循環次數;
      • 循環比較,從左到右,每一段相比(Bidecimal相比),某段大的則版本大,若段數不夠則用0代替;
  • 注意
    • 每段都是數字值相比,不是按字符串相比
    • 段數的多或少並不能說明版本的大小,比如並不是段數多的版本大;
    • 段是從左到右比的,哪段先大就版本大;

2.8 日誌

  • flyway本身提供了日誌的方式,但實現也比較混亂
    • 比如可以通過命令行參數指定-outputFile=xxx或-logFile=xxx來輸出日誌文件,通過指定-X或-q來分別控制日誌的級別爲DEBUG或WARN,但這些參數生效的前提是在環境中找不到slf4j等常用的日誌方式纔會生效
    • 這些日誌的格式無法控制,打印出來的日誌時間沒有日期
  • 使用logback
    • 依賴slf4j相關的三個包:logback-classic-1.2.3.jar、logback-core-1.2.3.jar、slf4j-api-1.7.25.jar
      • java代碼工程環境直接依賴即可
      • 命令行環境則需要把這些包放到${INSTALLDIR}/lib,可能也可以用其它方式依賴(待研究);
    • logback.xml文件路徑
      • 在java代碼工程環境中,如果是使用springboot環境,則直接放到classpath中即可找到(springboot有自己找logback的方式),否則依賴slf4j的查找方式(待研究);
      • 在命令行環境中,可在java命令啓動的時候加入-Dlogback.configurationFile=xxx/logback.xml 參數來指定logback.xml文件(不確定是否有更優雅的方式,待研究)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章