Spring Boot 2 實戰:使用 Flyway 管理你資料庫的版本變更

碼農小胖哥發表於2019-10-30

1. 前言

隨著專案的不斷迭代,資料庫表結構、資料都在發生著變化。甚至有的業務在多環境版本並行執行。資料為王的時代,管理好資料庫的版本也成為了迫切的需要。如何能做到像 Git 之類的版本控制工具來管理資料庫?Java 專案中常用 FlywayLiquibase 來管理資料庫版本。其中 Flyway 相對來說比較受歡迎。

2. Flyway 的特點

Flyway 大受歡迎是因為它具有以下優點:

  • 簡單 非常容易安裝和學習,同時遷移的方式也很容易被開發者接受。
  • 專一 Flyway 專注於搞資料庫遷移、版本控制而並沒有其它副作用。
  • 強大 專為連續交付而設計。讓Flyway在應用程式啟動時遷移資料庫。

3. Flyway 的工作機制

Flyway 需要在 DB 中先建立一個 metadata 表 (預設表名為 flyway_schema_history), 在該表中儲存著每次 migration (遷移)的記錄, 記錄包含 migration 指令碼的版本號和 SQL 指令碼的 checksum 值。下圖表示了多個資料庫版本。

對應的 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 掃描檔案系統或應用程式的類路徑讀取 DDLDML 以進行遷移。根據metadata 表進行檢查遷移。如果指令碼宣告的版本號小於或等於標記為當前版本的版本號之一,將忽略它們。其餘遷移是待處理遷移:可用,但未應用。最後按版本號對它們進行排序並按順序執行 並將執行結果寫入 metadata 表。

對應的 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 ,也支援構建工具 MavenGradle 。這裡我們將目光放在 Java Api 上。

3. Flyway 的規則

Flyway 是如何比較兩個 SQL 檔案的先後順序呢?它採用 採用左對齊原則, 缺位用 0 代替 。舉幾個例子:

1.0.1.1 比 1.0.1 版本高。

1.0.10 比 1.0.9.4 版本高。

1.0.10 和 1.0.010 版本號一樣高, 每個版本號部分的前導 0 會被忽略。

FlywaySQL 檔案分為 VersionedRepeatableUndo 三種:

  • Versioned 用於版本升級, 每個版本有唯一的版本號並只能執行一次.
  • Repeatable 可重複執行, 當 Flyway檢測到 Repeatable 型別的 SQL 指令碼的 checksum 有變動, Flyway 就會重新應用該指令碼. 它並不用於版本更新, 這類的 migration 總是在 Versioned 執行之後才被執行。
  • Undo 用於撤銷具有相同版本的版本化遷移帶來的影響。但是該回滾過於粗暴,過於機械化,一般不推薦使用。一般建議使用 Versioned 模式來解決。

這三種的命名規則如下圖:

naming.png

  • Prefix 可配置,字首標識,預設值 V 表示 Versioned, R 表示 Repeatable, U 表示 Undo
  • Version 標識版本號, 由一個或多個數字構成, 數字之間的分隔符可用點 . 或下劃線 _
  • Separator 可配置, 用於分隔版本標識與描述資訊, 預設為兩個下劃線 __
  • Description 描述資訊, 文字之間可以用下劃線 _ 或空格 分隔
  • Suffix 可配置, 後續標識, 預設為 .sql

4. Spring Boot 整合 Flyway

Spring Boot 提供了對 Flyway 的自動配置 。使我們可以開箱即用 Flyway 進行資料庫版本控制。

4.1 Flyway 依賴

你只需要引入依賴:

      <!-- 無需版本號 -->
 <dependency>
      <groupId>org.flywaydb</groupId>
      <artifactId>flyway-core</artifactId>
 </dependency>      

當然你要整合你的相關資料庫環境。這裡我們採用 H2 資料庫來演示,其它資料庫同理只不過方言不同。不熟悉 H2 資料庫的可參閱我的專題文章 Spring Boot 2 實戰:H2資料庫整合以及使用

4.2 Flyway 配置

為了直觀的講解配置,首先在 Spring Boot 配置檔案 application.yml 我們配置 H2 資料庫為:

 spring:
   datasource:
     #  h2 驅動
     driver-class-name: org.h2.Driver
     # h2  資料庫 持久化到磁碟D:/h2 庫名: flyway  mysql模式
     url: jdbc:h2:file:D:/h2/flyway;MODE=MySQL;DATABASE_TO_LOWER=TRUE
   h2:
     #    開啟console 訪問 預設false
     console:
       enabled: true
       settings:
         #      開啟h2 console 跟蹤 方便除錯  預設 false
         trace: true
         #      允許console 遠端訪問 預設false
         web-allow-others: true
       #  h2 訪問路徑上下文
       path: /h2-console

對應Flyway的配置為:

# flyway 配置
spring:
  flyway:
    # 啟用或禁用 flyway
    enabled: true
    # flyway 的 clean 命令會刪除指定 schema 下的所有 table, 生產務必禁掉。這個預設值是 false 理論上作為預設配置是不科學的。
    clean-disabled: true
    # SQL 指令碼的目錄,多個路徑使用逗號分隔 預設值 classpath:db/migration
    locations: classpath:db/migration
    #  metadata 版本控制資訊表 預設 flyway_schema_history
    table: flyway_schema_history
    # 如果沒有 flyway_schema_history 這個 metadata 表, 在執行 flyway migrate 命令之前, 必須先執行 flyway baseline 命令
    # 設定為 true 後 flyway 將在需要 baseline 的時候, 自動執行一次 baseline。
    baseline-on-migrate: true
    # 指定 baseline 的版本號,預設值為 1, 低於該版本號的 SQL 檔案, migrate 時會被忽略
    baseline-version: 1
    # 字元編碼 預設 UTF-8
    encoding: UTF-8
    # 是否允許不按順序遷移 開發建議 true  生產建議 false
    out-of-order: false
    # 需要 flyway 管控的 schema list,這裡我們配置為flyway  預設的話, 使用spring.datasource.url 配置的那個 schema,
    # 可以指定多個schema, 但僅會在第一個schema下建立 metadata 表, 也僅在第一個schema應用migration sql 指令碼.
    # 但flyway Clean 命令會依次在這些schema下都執行一遍. 所以 確保生產 spring.flyway.clean-disabled 為 true
    schemas: flyway
    # 執行遷移時是否自動呼叫驗證   當你的 版本不符合邏輯 比如 你先執行了 DML 而沒有 對應的DDL 會丟擲異常
    validate-on-migrate: true

請務必仔細閱讀 Flyway 相關配置的說明。

4.3 編寫 SQL 初始化指令碼

我們先編寫一個初始化 SQL 檔案,向 H2 資料庫已經自動初始化的 schema flyway 新增一張 sys_user 表。請注意命名規則。指令碼名稱為 V1.0.1__Add_table_user.sqlSQL 指令碼的位置在配置的 spring.flyway.locations 下。內容為:

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);

啟動 Spring Boot 應用 。開啟 H2 資料庫控制檯 http://localhost:8080/h2-console ,在 JDBC URL 一欄貼上 jdbc:h2:file:D:/h2/flyway;MODE=MySQL;DATABASE_TO_LOWER=TRUE 並點選 Connect 按鈕會進入以下介面:

這裡 -1 是因為我們預設了 Flyway 需要的 flyway_schema_history 表 。0 是因為 H2 資料庫自動初始化了 Schema flyway ,其它資料庫可能需要你手動來建立。

4.4 編寫 SQL 變更指令碼

我們編寫一個 V1.0.0__Delete_sysuser_felordcn.sql 來刪除 V1.0.1__Add_table_user.sql 中初始化的使用者。你會發現啟動報錯了,因為我們開啟了校驗,所以對於邏輯錯誤的版本會丟擲異常。我們將版本號更改為 V1.0.2__Delete_sysuser_felordcn.sql 再次啟動。通過 H2 資料庫控制檯我們會發現多了一條變更記錄:

同時 sys_user 表的資料也沒有了,符合預期。

5. Flyway 最佳實踐

通過上面的介紹相信你很快就會使用 Flyway 進行資料庫版本控制了。這裡總結了一些在實際開發中的使用經驗:

  1. 生產務必禁 spring.flyway.cleanDisabled=false
  2. 儘量避免使用 Undo 模式。
  3. 開發版本號儘量根據團隊來進行多層次的命名避免混亂。比如 V1.0.1__ProjectName_{Feature|fix}_Developer_Description.sql ,這種命名同時也可以獲取更多指令碼的開發者和相關功能的資訊。
  4. spring.flyway.outOfOrder 取值 生產上使用 true,開發中使用 false
  5. 多個系統公用一個 資料庫 schema 時配置spring.flyway.table 為不同的系統設定不同的 metadata 表名而不使用預設值 flyway_schema_history

6. 總結

今天我們對 Flyway 資料庫版本遷移管理工具進行了介紹並將之與 Spring Boot 相結合。這將大大規範我們的資料庫管理,提高生產效率。同時也分享了一些相當有用的生產實踐經驗。

相關的 DEMO 可通過關注公眾號:Felordcn 回覆 flyway 進行獲取。

關注公眾號:Felordcn 獲取更多資訊

個人部落格:https://felord.cn

相關文章