Flyway讓資料庫版本管理更簡單

恆生LIGHT雲社群發表於2021-12-15

作者:燒雞太子爺

來源: 恆生LIGHT雲社群

Flyway是什麼

隨著專案CICD接入,一鍵啟動,敏捷開發已經成為降本提效的不二法寶,其中涉及SQL的變更還不夠智慧和自動化,因此亟需一款工具能夠幫助開發及運維人員高效簡便地完成SQL變更,Flyway正是可以滿足我們需求的一款工具。

當我們開啟Flyway的官網,在首頁可以看到關於Flyway的一段英文介紹:

flyway--1.png

Flyway是資料庫的版本控制,跨所有環境的穩健架構演變。輕鬆、愉快和簡單的SQL。

flyway--2.png

總的來說,Flyway是一款專為CI/CD打造的,能對資料庫變更做版本控制的工具。

Flyway支援的資料庫很多,主流的資料庫都能夠完美支援,官網摘抄如下:

Supported databases are Oracle, SQL Server (including Amazon RDS and Azure SQL Database), Azure Synapse

(Formerly Data Warehouse), DB2, MySQL (including Amazon RDS, Azure Database & Google Cloud SQL), Aurora MySQL,

MariaDB, Percona XtraDB Cluster, TestContainers, PostgreSQL (including Amazon RDS, Azure Database, Google Cloud

SQL, TimescaleDB, YugabyteDB & Heroku), Aurora PostgreSQL, Redshift, CockroachDB, SAP HANA, Sybase ASE,

Informix, H2, HSQLDB, Derby, Snowflake, SQLite and Firebird.

Flyway解決的問題

在專案或產品研發過程中,很難一開始就把業務理清楚,把程式碼邏輯和資料庫表設計好,因此程式碼和資料表也會在迭代週期內不斷迭代。我們都習慣使用SVN或者Git來對程式碼進行版本管理,主要是為了解決多人開發程式碼衝突和版本回退的問題。

其實,資料庫的變更也需要版本控制,在日常開發和環境部署中,我們經常會遇到下面的問題:

  • 在開發環境部署程式發現報錯,定位是自己寫的SQL指令碼忘了在當前環境執行導致;

  • 從Git上新down下來的程式碼執行報錯,定位發現是其他同事修改了的SQL指令碼沒有在當前環境執行導致;

  • 每次釋出包都需要釋出SQL檔案包和應用程式的版本包;

  • 線上環境部署報錯,發現是運維沒有按照你投產文件裡面說明的SQL指令碼執行順序操作執行導致;

  • 流水線可以自動化部署程式,但是SQL指令碼還是需要手動執行或者流水線執行SQL指令碼配置比較繁瑣;

  • 其他場景....

有了Flyway,這些問題都可以輕鬆的解決。Flyway可以對資料庫進行版本管理,可以自動執行SQL,能快速有效地用於迭代資料庫表結構,並保證部署到測試環境或生產環境時,資料表都是保持一致的;

Flyway主要工作流程

Flyway工作流程如下:

flyway--3.png

1、專案啟動,應用程式完成資料庫連線池的建立後,Flyway自動執行。

2、初次使用時,Flyway會建立一個flyway_schema_history表,用於記錄sql執行記錄。

3、Flyway會掃描專案指定路徑下(預設是classpath:db/migration)的所有sql指令碼,與flyway_schema_history表指令碼記錄進行比對。如果資料庫記錄執行過的指令碼記錄,與專案中的sql指令碼不一致,Flyway會報錯並停止專案執行。

4、如果校驗透過,則根據表中的sql記錄最大版本號,忽略所有版本號不大於該版本的指令碼。再按照版本號從小到大,逐個執行其餘指令碼。

##在SpringBoot專案使用Flyway

Flyway既支援使用客戶端command-lineclient命令列方式也支援JAVA API方式升級資料庫,本文只介紹JAVA API以Maven引入外掛方式的使用,更多方式可以檢視官網;

引入依賴

在start.spring.io上新建一個SpringBoot工程,引入資料庫驅動依賴等,同時引入Flyway的依賴,這個步驟比較簡單,不做過多說明(本文示例使用的是Mysql資料庫);

<dependency>


  <groupId>org.flywaydb</groupId>

  <artifactId>flyway-core</artifactId>

  <version>7.14.0</version><!-- 截至發稿前,官網最新版本是7.14.0 -->

</dependency>

新增Flyway配置

spring:


# 資料庫連線配置

datasource:

  driver-class-name: com.mysql.cj.jdbc.Driver

  url: jdbc:mysql://localhost:3306/xukj_flyway?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT

  username: flyway

  password: xxx

flyway:

  # 是否啟用flyway

  enabled: true

      # 編碼格式,預設UTF-8

  encoding: UTF-8

  # 遷移sql指令碼檔案存放路徑,預設db/migration

  locations: classpath:db/migration

  # 遷移sql指令碼檔名稱的字首,預設V

  sql-migration-prefix: V

  # 遷移sql指令碼檔名稱的分隔符,預設2個下劃線__

  sql-migration-separator: __

  # 遷移sql指令碼檔名稱的字尾

  sql-migration-suffixes: .sql

  # 遷移時是否進行校驗,預設true

  validate-on-migrate: true

  # 當遷移發現資料庫非空且存在沒有後設資料的表時,自動執行基準遷移,新建schema_version表

  baseline-on-migrate: true

建立SQL指令碼

專案建立以後,在src/resources下有 db/migration(或db.migration)目錄,在其中建立對應的SQL檔案,基於約定由於配置的原則,不同的型別透過檔案命名方式進行區分

flyway--4.png

Flyway對資料庫的所有更改都稱為遷移;版本遷移(Versioned Migrations)以V開頭,只會執行一次;回退遷移(Undo Migrations)以U開頭,執行一旦發生破壞性更改,就會很麻煩,專案中一般不用;可重複執行遷移(Repeatable Migrations)以R開頭,每次修改後都會重新執行。

總結如下:

  1. 僅需要被執行一次的SQL命名以大寫的"V"開頭,後面跟上"0~9"數字的組合,數字之間可以用“.”或者下劃線"_"分割開,然後再以兩個下劃線分割,其後跟檔名稱,最後以.sql結尾。比如,V2020.00.000_1 create_table.sql,V202001.00.000_2 insertTable.sql,V2.1.5__create_table.sql。

  2. 可重複執行的SQL,則以大寫的“R”開頭,後面再以兩個下劃線分割,其後跟檔名稱,最後以.sql結尾。比如,R addTable.sql,R update_user.sql。

  3. 版本號需要唯一,否則Flyway執行會報錯;如果V__指令碼.sql,已經執行過了,不能修改裡面的內容,再次執行Flyway就會報錯。R——指令碼.sql,如有變化可以執行多次。

  4. V開頭的SQL執行優先順序要比R開頭的SQL優先順序高。

如下,我們準備了三個指令碼,分別為:

1、V2020.00.000_1__create_table.sql,程式碼如下,目的是建立一張表,且只執行一次

CREATE TABLE `test_flyway_table` (


`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',

`time` datetime NOT NULL COMMENT '建立時間',

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、V202001.00.000_2__insertTable.sql,程式碼如下,目的是往表中插入一條資料,且只執行一次

INSERT INTO `test_flyway_table` VALUES ('1', '2021-06-28 17:48:48');

3、R__addTable.sql,程式碼如下,目的是每次啟動如果有變化,則執行一次

update `test_flyway_table` set time = '2021-08-23 17:48:48' where id =1;

對應目錄截圖如下:

flyway--5.png

執行

按照上面配置完成,已經足夠我們開始執行了,此時,我們第一次啟動專案,如果配置沒有錯誤,執行截圖如下:

flyway--6.png

此時,我們重新整理資料庫,可以看到Flyway的歷史記錄表已經生成並插入了三個版本的記錄:

flyway--7.png

而且,表也已經建立好了並有一條資料:

flyway--8-1.png

此時不做任何改變,重啟程式,日誌如下:

flyway--9.png

日誌顯示錶沒有變化,不做變更,檢視兩張表的內容也無變化。

如果我們修改V202001.00.000_2__insertTable.sql指令碼,重啟程式,就會報錯,提示資訊如下:

Caused by: org.flywaydb.core.api.exception.FlywayValidateException: Validate failed: Migrations have failed validation


Migration checksum mismatch for migration version 202001.00.000.2

-> Applied to database : 1190158040

-> Resolved locally: 843339817. Either revert the changes to the migration, or run repair to update the schema history.

如果我們修改R__addTable.sql指令碼,重啟程式,指令碼會再次執行,並且Flyway的歷史記錄表也會增加本次執行的記錄。

Flyway的執行效率

為了驗證Flyway專案資料庫迭代了較久時間,積累了幾百個Sql以後是否會拖慢專案的啟動速度?進行了一個對照組試驗:

場景(sql檔案控制在10K以內) 執行平均時長(單位:秒)
放1個已經被執行過的SQL指令碼,反覆啟動專案 11.1
放25個已經被執行過的SQL指令碼,反覆啟動專案 11.4
放50個已經被執行過的SQL指令碼,反覆啟動專案 11.6
放100個已經被執行過的SQL指令碼,反覆啟動專案 12.19

指令碼在歷史記錄表中有記錄,即使有幾百條SQL指令碼,每次專案啟動只執行單次迭代的指令碼,所以耗時主要來源於兩個方面:

  • Flyway依次讀取指令碼中內容時的IO開銷;

  • Flyway計算指令碼checksum值的演算法開銷;

對於IO開銷而言,每個指令碼如果不是涉及大量的資料變更,只是表結構的變更,指令碼的大小都非常小,可以不考慮.事實上Flyway也不適合大量的資料變更時使用,因此IO開銷對啟動耗時的增量基本可以忽略

Flyway計算checksum值採用的是著名的CRC32(迴圈冗餘校驗碼)演算法,該演算法是資料通訊領域中最常用的一種查錯校驗碼,其特徵是資訊欄位和校驗欄位的長度可以任意選定。迴圈冗餘檢查(CRC)是一種資料傳輸檢錯功能,對資料進行多項式計算,並將得到的結果附在幀的後面,接收裝置也執行類似的演算法,以保證資料傳輸的正確性和完整性。為了驗證演算法計算效率,做了如下試驗:

場景 執行平均時長(單位:毫秒)
讀取一個超大檔案,約1M,並進行計算,反覆執行多次 48
讀取一個正常大小的SQL指令碼,約10K,並進行計算,反覆執行多次 4

由此可見,即便是有上百個正常大小的sql,計算checksum值也不會耗費太多的時間,基本都可以在1秒內完成,所以接入Flyway後也不必擔心會有歷史包袱。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70001864/viewspace-2847796/,如需轉載,請註明出處,否則將追究法律責任。

相關文章