Spring Boot 2 實(shí)戰(zhàn):使用 Flyway 管理你數(shù)據(jù)庫(kù)的版本變更

1. 前言

隨著項(xiàng)目的不斷迭代,數(shù)據(jù)庫(kù)表結(jié)構(gòu)、數(shù)據(jù)都在發(fā)生著變化。甚至有的業(yè)務(wù)在多環(huán)境版本并行運(yùn)行。數(shù)據(jù)為王的時(shí)代,管理好數(shù)據(jù)庫(kù)的版本也成為了迫切的需要。如何能做到像 Git 之類的版本控制工具來(lái)管理數(shù)據(jù)庫(kù)?Java 項(xiàng)目中常用 FlywayLiquibase 來(lái)管理數(shù)據(jù)庫(kù)版本。其中 Flyway 相對(duì)來(lái)說(shuō)比較受歡迎。

2. Flyway 的特點(diǎn)

Flyway 大受歡迎是因?yàn)樗哂幸韵聝?yōu)點(diǎn):

  • 簡(jiǎn)單 非常容易安裝和學(xué)習(xí),同時(shí)遷移的方式也很容易被開發(fā)者接受。
  • 專一 Flyway 專注于搞數(shù)據(jù)庫(kù)遷移、版本控制而并沒有其它副作用。
  • 強(qiáng)大 專為連續(xù)交付而設(shè)計(jì)。讓Flyway在應(yīng)用程序啟動(dòng)時(shí)遷移數(shù)據(jù)庫(kù)。

3. Flyway 的工作機(jī)制

Flyway 需要在 DB 中先創(chuàng)建一個(gè) metadata 表 (缺省表名為 flyway_schema_history), 在該表中保存著每次 migration (遷移)的記錄, 記錄包含 migration 腳本的版本號(hào)和 SQL 腳本的 checksum 值。下圖表示了多個(gè)數(shù)據(jù)庫(kù)版本。

對(duì)應(yīng)的 metadata 表記錄:

installed_rank version description type script checksum installed_by installed_on execution_time success
1 1 Initial Setup SQL V1__Initial_Setup.sql 1996767037 axel 2016-02-04 22:23:00.0 546 true
2 2 First Changes SQL V2__First_Changes.sql 1279644856 axel 2016-02-06 09:18:00.0 127 true

Flyway 掃描文件系統(tǒng)或應(yīng)用程序的類路徑讀取 DDLDML 以進(jìn)行遷移。根據(jù)metadata 表進(jìn)行檢查遷移。如果腳本聲明的版本號(hào)小于或等于標(biāo)記為當(dāng)前版本的版本號(hào)之一,將忽略它們。其余遷移是待處理遷移:可用,但未應(yīng)用。最后按版本號(hào)對(duì)它們進(jìn)行排序并按順序執(zhí)行 并將執(zhí)行結(jié)果寫入 metadata 表。

對(duì)應(yīng)的 metadata 表記錄:

installed_rank version description type script checksum installed_by installed_on execution_time success
1 1 Initial Setup SQL V1__Initial_Setup.sql 1996767037 axel 2016-02-04 22:23:00.0 546 true
2 2 First Changes SQL V2__First_Changes.sql 1279644856 axel 2016-02-06 09:18:00.0 127 true
3 2.1 Refactoring JDBC V2_1__Refactoring axel 2016-02-10 17:45:05.4 251 true

Flyway 支持命令行(需要下載命令行工具)和 Java Api ,也支持構(gòu)建工具 MavenGradle 。這里我們將目光放在 Java Api 上。

3. Flyway 的規(guī)則

Flyway 是如何比較兩個(gè) SQL 文件的先后順序呢?它采用 采用左對(duì)齊原則, 缺位用 0 代替 。舉幾個(gè)例子:

1.0.1.1 比 1.0.1 版本高。

1.0.10 比 1.0.9.4 版本高。

1.0.10 和 1.0.010 版本號(hào)一樣高, 每個(gè)版本號(hào)部分的前導(dǎo) 0 會(huì)被忽略。

FlywaySQL 文件分為 Versioned 、RepeatableUndo 三種:

  • Versioned 用于版本升級(jí), 每個(gè)版本有唯一的版本號(hào)并只能執(zhí)行一次.
  • Repeatable 可重復(fù)執(zhí)行, 當(dāng) Flyway檢測(cè)到 Repeatable 類型的 SQL 腳本的 checksum 有變動(dòng), Flyway 就會(huì)重新應(yīng)用該腳本. 它并不用于版本更新, 這類的 migration 總是在 Versioned 執(zhí)行之后才被執(zhí)行。
  • Undo 用于撤銷具有相同版本的版本化遷移帶來(lái)的影響。但是該回滾過(guò)于粗暴,過(guò)于機(jī)械化,一般不推薦使用。一般建議使用 Versioned 模式來(lái)解決。

這三種的命名規(guī)則如下圖:

naming.png
  • Prefix 可配置,前綴標(biāo)識(shí),默認(rèn)值 V 表示 Versioned, R 表示 Repeatable, U 表示 Undo
  • Version 標(biāo)識(shí)版本號(hào), 由一個(gè)或多個(gè)數(shù)字構(gòu)成, 數(shù)字之間的分隔符可用點(diǎn) . 或下劃線 _
  • Separator 可配置, 用于分隔版本標(biāo)識(shí)與描述信息, 默認(rèn)為兩個(gè)下劃線 __
  • Description 描述信息, 文字之間可以用下劃線 _ 或空格 分隔
  • Suffix 可配置, 后續(xù)標(biāo)識(shí), 默認(rèn)為 .sql

4. Spring Boot 集成 Flyway

Spring Boot 提供了對(duì) Flyway 的自動(dòng)配置 。使我們可以開箱即用 Flyway 進(jìn)行數(shù)據(jù)庫(kù)版本控制。

4.1 Flyway 依賴

你只需要引入依賴:

      <!-- 無(wú)需版本號(hào) -->
 <dependency>
      <groupId>org.flywaydb</groupId>
      <artifactId>flyway-core</artifactId>
 </dependency>      

當(dāng)然你要集成你的相關(guān)數(shù)據(jù)庫(kù)環(huán)境。這里我們采用 H2 數(shù)據(jù)庫(kù)來(lái)演示,其它數(shù)據(jù)庫(kù)同理只不過(guò)方言不同。不熟悉 H2 數(shù)據(jù)庫(kù)的可參閱我的專題文章 Spring Boot 2 實(shí)戰(zhàn):H2數(shù)據(jù)庫(kù)集成以及使用 。

4.2 Flyway 配置

為了直觀的講解配置,首先在 Spring Boot 配置文件 application.yml 我們配置 H2 數(shù)據(jù)庫(kù)為:

 spring:
   datasource:
     #  h2 驅(qū)動(dòng)
     driver-class-name: org.h2.Driver
     # h2  數(shù)據(jù)庫(kù) 持久化到磁盤D:/h2 庫(kù)名: flyway  mysql模式
     url: jdbc:h2:file:D:/h2/flyway;MODE=MySQL;DATABASE_TO_LOWER=TRUE
   h2:
     #    開啟console 訪問(wèn) 默認(rèn)false
     console:
       enabled: true
       settings:
         #      開啟h2 console 跟蹤 方便調(diào)試  默認(rèn) false
         trace: true
         #      允許console 遠(yuǎn)程訪問(wèn) 默認(rèn)false
         web-allow-others: true
       #  h2 訪問(wèn)路徑上下文
       path: /h2-console

對(duì)應(yīng)Flyway的配置為:

# flyway 配置
spring:
  flyway:
    # 啟用或禁用 flyway
    enabled: true
    # flyway 的 clean 命令會(huì)刪除指定 schema 下的所有 table, 生產(chǎn)務(wù)必禁掉。這個(gè)默認(rèn)值是 false 理論上作為默認(rèn)配置是不科學(xué)的。
    clean-disabled: true
    # SQL 腳本的目錄,多個(gè)路徑使用逗號(hào)分隔 默認(rèn)值 classpath:db/migration
    locations: classpath:db/migration
    #  metadata 版本控制信息表 默認(rèn) flyway_schema_history
    table: flyway_schema_history
    # 如果沒有 flyway_schema_history 這個(gè) metadata 表, 在執(zhí)行 flyway migrate 命令之前, 必須先執(zhí)行 flyway baseline 命令
    # 設(shè)置為 true 后 flyway 將在需要 baseline 的時(shí)候, 自動(dòng)執(zhí)行一次 baseline。
    baseline-on-migrate: true
    # 指定 baseline 的版本號(hào),默認(rèn)值為 1, 低于該版本號(hào)的 SQL 文件, migrate 時(shí)會(huì)被忽略
    baseline-version: 1
    # 字符編碼 默認(rèn) UTF-8
    encoding: UTF-8
    # 是否允許不按順序遷移 開發(fā)建議 true  生產(chǎn)建議 false
    out-of-order: false
    # 需要 flyway 管控的 schema list,這里我們配置為flyway  缺省的話, 使用spring.datasource.url 配置的那個(gè) schema,
    # 可以指定多個(gè)schema, 但僅會(huì)在第一個(gè)schema下建立 metadata 表, 也僅在第一個(gè)schema應(yīng)用migration sql 腳本.
    # 但flyway Clean 命令會(huì)依次在這些schema下都執(zhí)行一遍. 所以 確保生產(chǎn) spring.flyway.clean-disabled 為 true
    schemas: flyway
    # 執(zhí)行遷移時(shí)是否自動(dòng)調(diào)用驗(yàn)證   當(dāng)你的 版本不符合邏輯 比如 你先執(zhí)行了 DML 而沒有 對(duì)應(yīng)的DDL 會(huì)拋出異常
    validate-on-migrate: true

請(qǐng)務(wù)必仔細(xì)閱讀 Flyway 相關(guān)配置的說(shuō)明。

4.3 編寫 SQL 初始化腳本

我們先編寫一個(gè)初始化 SQL 文件,向 H2 數(shù)據(jù)庫(kù)已經(jīng)自動(dòng)初始化的 schema flyway 添加一張 sys_user 表。請(qǐng)注意命名規(guī)則。腳本名稱為 V1.0.1__Add_table_user.sqlSQL 腳本的位置在配置的 spring.flyway.locations 下。內(nèi)容為:

use `flyway`;
CREATE TABLE `sys_user`
(
    `user_id`         int(10) unsigned NOT NULL AUTO_INCREMENT,
    `username`        varchar(1024)    NOT NULL unique ,
    `encode_password` varchar(1024)       NOT NULL,
    `age`             int(3)           NOT NULL,
    PRIMARY KEY (`user_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

insert into  flyway.sys_user values (1,'Felordcn','{noop}12345',18);

啟動(dòng) Spring Boot 應(yīng)用 。打開 H2 數(shù)據(jù)庫(kù)控制臺(tái) http://localhost:8080/h2-console ,在 JDBC URL 一欄粘貼 jdbc:h2:file:D:/h2/flyway;MODE=MySQL;DATABASE_TO_LOWER=TRUE 并點(diǎn)擊 Connect 按鈕會(huì)進(jìn)入以下界面:

這里 -1 是因?yàn)槲覀內(nèi)笔×?Flyway 需要的 flyway_schema_history 表 。0 是因?yàn)?H2 數(shù)據(jù)庫(kù)自動(dòng)初始化了 Schema flyway ,其它數(shù)據(jù)庫(kù)可能需要你手動(dòng)來(lái)建立。

4.4 編寫 SQL 變更腳本

我們編寫一個(gè) V1.0.0__Delete_sysuser_felordcn.sql 來(lái)刪除 V1.0.1__Add_table_user.sql 中初始化的用戶。你會(huì)發(fā)現(xiàn)啟動(dòng)報(bào)錯(cuò)了,因?yàn)槲覀冮_啟了校驗(yàn),所以對(duì)于邏輯錯(cuò)誤的版本會(huì)拋出異常。我們將版本號(hào)更改為 V1.0.2__Delete_sysuser_felordcn.sql 再次啟動(dòng)。通過(guò) H2 數(shù)據(jù)庫(kù)控制臺(tái)我們會(huì)發(fā)現(xiàn)多了一條變更記錄:

同時(shí) sys_user 表的數(shù)據(jù)也沒有了,符合預(yù)期。

5. Flyway 最佳實(shí)踐

通過(guò)上面的介紹相信你很快就會(huì)使用 Flyway 進(jìn)行數(shù)據(jù)庫(kù)版本控制了。這里總結(jié)了一些在實(shí)際開發(fā)中的使用經(jīng)驗(yàn):

  1. 生產(chǎn)務(wù)必禁 spring.flyway.cleanDisabled=false
  2. 盡量避免使用 Undo 模式。
  3. 開發(fā)版本號(hào)盡量根據(jù)團(tuán)隊(duì)來(lái)進(jìn)行多層次的命名避免混亂。比如 V1.0.1__ProjectName_{Feature|fix}_Developer_Description.sql ,這種命名同時(shí)也可以獲取更多腳本的開發(fā)者和相關(guān)功能的信息。
  4. spring.flyway.outOfOrder 取值 生產(chǎn)上使用 false,開發(fā)中使用 true
  5. 多個(gè)系統(tǒng)公用一個(gè) 數(shù)據(jù)庫(kù) schema 時(shí)配置spring.flyway.table 為不同的系統(tǒng)設(shè)置不同的 metadata 表名而不使用缺省值 flyway_schema_history 。

6. 總結(jié)

今天我們對(duì) Flyway 數(shù)據(jù)庫(kù)版本遷移管理工具進(jìn)行了介紹并將之與 Spring Boot 相結(jié)合。這將大大規(guī)范我們的數(shù)據(jù)庫(kù)管理,提高生產(chǎn)效率。同時(shí)也分享了一些相當(dāng)有用的生產(chǎn)實(shí)踐經(jīng)驗(yàn)。

** 相關(guān)的 DEMO 可通過(guò)關(guān)注公眾號(hào):Felordcn 回復(fù) flyway 進(jìn)行獲取。**

關(guān)注公眾號(hào):碼農(nóng)小胖哥,獲取更多資訊

個(gè)人博客:https://felord.cn

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容