Flyway基于HighGoDB實現(xiàn)數(shù)據(jù)庫版本管理

1. 我們?yōu)槭裁葱枰獢?shù)據(jù)庫的版本集成?

假設(shè)我們有一個名為demo的項目,項目的主要交付物就是提供一塊叫做demo soft的代碼并連接到名為demo db的數(shù)據(jù)庫。

如果用一個圖來表示上述所意的,應(yīng)該如下:

image

現(xiàn)在我們有了代碼和數(shù)據(jù)庫,這也是大多數(shù)初創(chuàng)項目所需的所有內(nèi)容了。

但是隨著項目的進展,這種簡單的開發(fā)模式馬上會演變成如下的這種樣子:

image

現(xiàn)在我們不止一個環(huán)境,而是有了開發(fā)、測試、灰度、生產(chǎn)等,由于環(huán)境數(shù)量的增加,給我們帶來了更多的挑戰(zhàn)。

git、svn等版本管理工具功能強大,可以很好的幫助我們管理代碼。

但是,我們的在數(shù)據(jù)庫的版本管理方面做的不那么的好。很多項目還是依靠dba或是scm手動的執(zhí)行sql腳本,而有的時候,為了臨時的解決某些問題,會在這個或是那個環(huán)境手動的執(zhí)行某些腳本,如此這樣就會產(chǎn)生很多的問題。比如我如何知道某臺機器上面的數(shù)據(jù)庫處于什么狀態(tài)。sql腳本文件是執(zhí)行了,還是沒有執(zhí)行?在生產(chǎn)環(huán)境執(zhí)行的腳本文件隨后在其他的環(huán)境有執(zhí)行嗎?如何啟動一臺全新的數(shù)據(jù)庫實例,而與其他的數(shù)據(jù)庫實例保持一致?

數(shù)據(jù)庫的持續(xù)集成是解決這一混亂問題的好方法,它具有如下優(yōu)點

  1. 重頭重新的創(chuàng)建數(shù)據(jù)庫
  2. 清楚的知道你的數(shù)據(jù)庫現(xiàn)在處于什么樣的狀態(tài)
  3. 以確定的方式從當前版本的數(shù)據(jù)庫集成到新的版本

2.Flyway的主要特征

Text
普通 SQL:純 SQL 腳本(包括占位符替換)沒有專有的XML格式,沒有鎖定
無限制:使用 Java 代碼來進行一些高級數(shù)據(jù)操作
零依賴:只需運行在 Java6(及以上)和數(shù)據(jù)庫所需的 JDBC 驅(qū)動
約定優(yōu)于配置:遷移時,自動查找系統(tǒng)文件和類路徑中的 SQL 文件或 Java 類
高可靠性:在集群環(huán)境下進行數(shù)據(jù)庫升級是安全可靠的
云支持:完全支持 Microsoft SQL Azure, Google Cloud SQL & App Engine、Heroku Postgres 和 Amazon RDS
自動遷移:使用 Flyway 提供的 API,讓應(yīng)用啟動和遷移同時工作
快速失敗:損壞的數(shù)據(jù)庫或失敗的遷移可以防止應(yīng)用程序啟動
數(shù)據(jù)庫清理:在一個數(shù)據(jù)庫中刪除所有的表、視圖、觸發(fā)器,而不是刪除數(shù)據(jù)庫本身

3.Flyway的工作原理

假設(shè)一個最簡單的場景,我們要在一個空的數(shù)據(jù)庫上面應(yīng)用flyway。

image

flyway首先在數(shù)據(jù)庫上面找一個默認的表名為flyway_schema_history的歷史記錄表,由于此時,數(shù)據(jù)庫為空,所以flyway會創(chuàng)建一個表名為flyway_schema_history的空表。

image

然后flyway會使用此表來跟蹤和記錄數(shù)據(jù)庫的日志狀態(tài)。

創(chuàng)建了flyway_schema_history之后,flyway馬上會掃描文件系統(tǒng)或是web應(yīng)用下的classpath路徑下的sql文件(如下的介紹默認使用sql文件做為migrations文件)或是java文件為migration做準備。

找到migrations文件之后,會按照version number升序排列,然后依次的執(zhí)行migrations文件。

image

每執(zhí)行一次migrations文件,flyway_schema_history表的內(nèi)容相應(yīng)的就發(fā)生變化。

image

如果數(shù)據(jù)庫已經(jīng)有了flyway_schema_history表,且其中記錄了相應(yīng)的版本信息,我們現(xiàn)在看一下flyway_schema_history是如何做版本升級的。flyway馬上會掃描文件系統(tǒng)或是web應(yīng)用下的classpath路徑下的migrations文件為migration做準備。如果掃描到的migrations文件的版本號小于或是等于當前flyway_schema_history記錄的標本號,這些migrations文件會被忽略。而剩下的migrations文件就是準備執(zhí)行的migrations文件。

image

這些剩余的migrations文件會按照version number升序排列并執(zhí)行,flyway_schema_history也會相應(yīng)的記錄版本更新的情況。

image

所以如果你想升級數(shù)據(jù)庫的版本信息,不管是DDL或是DML語句,只要生成一個migrations文件,并將其version number大于當前的flyway_schema_history記錄的當前版本信息即可,當下次flyway執(zhí)行的時候,就會找到新增的這些migrations文件,并執(zhí)行和記錄新的版本信息。通過這種方式,可以很方便的將相關(guān)的升級腳本與代碼一同的發(fā)布。

4.Flyway配置屬性詳解

Text
Url:連接數(shù)據(jù)庫的jdbc的url
Driver:連接數(shù)據(jù)庫的jdbc驅(qū)動的class全名,默認為空,flyway會根據(jù)url?動查找匹配的驅(qū)動
User:連接數(shù)據(jù)庫的?戶名
Password:連接數(shù)據(jù)庫的?戶名對應(yīng)的密碼
connectRetries:連接數(shù)據(jù)庫的最?的重試次數(shù),每次嘗試失敗后,flyway會等待?秒然后繼續(xù)重試到最?次數(shù)為?(不設(shè)置,就不重試,快速的失敗)
initSql:連接上數(shù)據(jù)庫之后的初始化sql
defaultSchema:默認的schema,??寫敏感,是flyway在執(zhí)?過程中默認的schema,flyway_schema_history包含在這個schema??,flyway的6.1版本之后,如果沒有指定schema,那么默認使?schemas屬性配置的第?個schema
Schemas:使?逗號分隔,??寫敏感,除?配置的第?個schema已經(jīng)存在,不然會創(chuàng)建所有的schema,所有的schema會按照順序做clean操作
Table:默認名flyway_schema_history,如果在schemas配置了多個schema,那么flyway_schema_history表在第?個schema??
Tablespace:創(chuàng)建flyway_schema_history的表空間,此設(shè)置僅與?持表空間概念的數(shù)據(jù)庫相關(guān)。對其它數(shù)據(jù)庫不起作?
Locations:locations使?逗號分隔,會對其指定的location進?遞歸查找,location的類型依配置的location的前綴?定,沒有前綴的location或是以classpath:標記的location會在classpath上掃描sql或是java?件,以filesysytem標記的location會在?件系統(tǒng)上遞歸的查找?隱藏的migration?件,?且可以使?通配符
Color:僅僅應(yīng)?于命令?使?,是否可以對于輸出添加顏?,默認是auto,可以有always和never其它的兩個選項
jarDirs:逗號分隔,驅(qū)動?件和基于Java的migration?件所在?錄
sqlMigrationPrefix:migration?件的前綴,默認為V
undoSqlMigrationPrefix:undo?件前綴,默認是U
repeatableSqlMigrationPrefix:repeat?件前綴,默認是P
sqlMigrationSeparator:migration?件分隔符,默認是__
sqlMigrationSuffixes:migration?件后綴,默認是.sql
validateMigrationNaming:是否驗證migration?件,默認是false,如果migration?件名格式不滿?要求,skip,如果為true,快速失敗
Stream:是否對migration?件流化處理,?不是全部的加載到內(nèi)存之后再處理(如果migratio?件很?,如1GB,?般也不會遇到這樣的情況)
Batch:是否對migration?件進?批處理,可以節(jié)約帶寬,對于處理?的migration?件??
Encoding:migration?件編碼
placeholderReplacement:是否進?占位符替換,默認為true
placeholderPrefix:占位符前綴,默認為${
placeholderSuffix:占位符后綴,默認為}
Resolvers:migration?件解析器,逗號分隔的全class?件名,相當于可以擴展內(nèi)置的resolvers解析器
skipDefaultResolvers:是否skip內(nèi)置的解析器只使?定制化的解析器,默認是false
callbacks:逗號分隔的class?件名,在flyway?命周期內(nèi)被回調(diào)引?
skipDefaultCallbacks:是否skip內(nèi)置的callbacks,默認false
target:migration時數(shù)據(jù)庫的版本,最好使?默認值
outOfOrder:是否可以?序執(zhí)?,維持默認即可
ignoreMissingMigrations:忽略丟失的migration?件,默認是false,針對?的系統(tǒng)可以進?

5.Flyway命令

Text
Migrate:遷移操作,對于每次數(shù)據(jù)庫進行版本遷移的操作。將sql腳本中的內(nèi)容進行執(zhí)行記錄	
Baseline:初始化 schema_version 表,并插入一條原始 version = 1 。實現(xiàn)在非空數(shù)據(jù)庫新建MetaData表,并把Migrations應(yīng)用到該數(shù)據(jù)庫;也可以在已有表結(jié)構(gòu)的數(shù)據(jù)庫中實現(xiàn)添加Metadata表。
Clean:清除掉對應(yīng)數(shù)據(jù)庫Schema中所有的對象,包括表結(jié)構(gòu),視圖,存儲過程等,clean操作在dev 和 test階段很好用,但在生產(chǎn)環(huán)境務(wù)必禁用。
Info:用于打印所有的Migrations的詳細和狀態(tài)信息,也是通過MetaData和Migrations完成的,可以快速定位當前的數(shù)據(jù)庫版本。
Repair:repair操作能夠修復(fù)metaData表,該操作在metadata出現(xiàn)錯誤時很有用。
Undo:撤銷操作,社區(qū)版不支持。
Validate:驗證已經(jīng)apply的Migrations是否有變更,默認開啟的,原理是對比MetaData表與本地Migrations的checkNum值,如果值相同則驗證通過,否則失敗。

6.Migration

Flyway將每一個數(shù)據(jù)庫腳本稱之為migrations,flyway支持三種類型的migration:

  1. Versioned migrations:最常用的migration,可以簡單的理解為數(shù)據(jù)庫升級腳本
  2. Undo migrations:數(shù)據(jù)庫版本回退腳本,需要Pro版本,忽略,而且使用過程存在較大風險,undo操作目前只能通過plugin或者command-line來執(zhí)行
  3. Repeatable migrations:可重復(fù)執(zhí)行的migration,例如create or replace腳本,當腳本checksums改變時會重新執(zhí)行

每個migration支持兩種編寫方式:

  1. Java:通過實現(xiàn) org.flywaydb.core.api.migration.jdbc.JdbcMigratio 接口來創(chuàng)建一個Java migration,也就是通過JDBC來執(zhí)行SQL,對于類是CLOB或者BOLB這種不方便在SQL中實現(xiàn)的腳本比較有用,例如:
package db.migration;
import org.flywaydb.core.api.migration.jdbc.JdbcMigration;
import java.sql.Connection;
import java.sql.PreparedStatement;
public class V1_2__Another_user implements JdbcMigration {
public void migrate(Connection connection) throws Exception {
PreparedStatement statement = connection.prepareStatement("INSERT INTO test_user (name) VALUES ('Obelix')");
try {
statement.execute();
} finally {
statement.close();
}
}
}
  1. SQL:簡單的SQL腳本文件,例如:
Text
CREATE TABLE test_user (
name VARCHAR(25) NOT NULL,
PRIMARY KEY(name)
);
INSERT INTO ${tableName} (name) VALUES ('Mr. T');

所有的migration都需要遵守命名規(guī)范:

image

確保版本號唯一,flyway按照版本號順序執(zhí)行。repeatable沒有版本號,因為repeatable migrations會在內(nèi)容改變時重復(fù)執(zhí)行。

7.基于spring boot的flyway實戰(zhàn)

  1. 在pom文件中引入依賴
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>6.0.8</version>
</dependency>
  1. application.yml配置

image

  1. 在resources的db/migration目錄下面添加sql腳本

image

  1. 啟動項目,查看控制臺輸出

image

8.Flyway jar包引用注意事項

報錯:{conn-10005, pstmt-20009} execute error. SET ROLE ‘sysdba’

企業(yè)版數(shù)據(jù)庫不會報錯,安全版數(shù)據(jù)庫報 set role 無權(quán)限錯誤。

解決方案:關(guān)閉安全版數(shù)據(jù)庫三權(quán)

#該參數(shù)配置重啟生效
psql -U syssso -d highgo
select set_secure_param('hg_sepofpowers','off');