api:http://flywaydb.org/getstarted/firststeps/api.html
1. 引言
想到要管理數據庫的版本,是在實際產品中遇到問題後想到的一種解決方案,當時各個環境的數據庫亂做一團,沒有任何一我的(開發、測試、維護人員)可以講清楚當前環境下的數據庫是哪一個版本,與哪一個版本的應用相匹配,如何升級到與新版本的應用相匹配。 html
想到管理數據庫版本時,先是心底造成了一個初步的解決方案,大體是經過數據庫中的某張表來記錄數據庫表結構的歷次更新與對應版本,在每次數據庫表結構調整時除了提供庫表更新sql ,還必須提供更新記錄與對應版本的sql,以幫助維護數據庫版本信息,並在遇到問題時提供相關的排查依據。 java
後來據此思路在網絡上搜索了一把,結果搜到Liquibase (另外一款開源數據庫版本管理工具)。在學習瞭解Liquibase 的時候,經高手介紹又瞭解到了Flyway 這個項目的存在。通過一番瞭解,最後咱們選擇了Flyway ,主要緣由是Flyway 支持sql 腳本,而Liquibase 只支持XML 方式的數據庫表結構定義,雖然在新的版本中號稱在XML的數據庫表結構定義方式中支持了sql 腳本。 mysql
雖然Flyway 的中文文檔近乎爲零,英文文檔也百裏挑一,但它倒是咱們最理想的數據庫版本管理工具,它不但支持sql 腳本,還支持Java 代碼直接操做數據庫(在版本升級時作數據遷移至關有用),有Maven 插件,支持命令行(咱們的平臺數據庫有部分由C 語言項目管理),並且在Spring 框架的配合下,很容易就能實現應用啓動時自動檢查並升級數據庫的功能。 spring
2. 什麼是Flyway
Flyway 是獨立於數據庫的應用、管理並跟蹤數據庫變動的數據庫版本管理工具。 sql
Flyway 的項目主頁是 http://flywaydb.org/ )(最近才遷移到這個主頁,以前一直在googlecode 下管理http://code.google.com/p/flyway/ ),在項目的主頁上能夠看到Flyway 與幾款主流數據庫版本管理工具的特性對比列表。 數據庫
3. 爲何使用Flyway
3.1. 咱們遇到的問題
咱們遇到的問題(如下內容來自Flyway 的一些英文文檔,從中抽取出來的咱們也遇到的一些主要問題): api
- 不一樣的開發人員在開發產品特性時,都有可能更新數據庫(添加新表,新的約束等)。當開發人員完成工做並提交代碼時,代碼會被合併到主分支並在測試服務器上執行單元測試與集成測試。咱們在哪一個環節來執行數據庫的更新操做呢?由QA 部門手工執行sql 腳本?或者咱們開發一斷程序自動執行數據庫更新?以什麼順序來執行這些更新腳本?這些問題一樣存在於生產環境。
- 咱們的產品部署在不一樣的客戶服務器上,以及不少的測試、聯調、實驗局、銷售環境上。不一樣的客戶和測試環境上都部署着不一樣版本的產品。當他們須要升級他們的產品到新的版本時,咱們不只須要讓他們的管理員能夠升級產品到新的版本,同時須要保留他們的已有數據。在升級產品的步驟中,咱們清楚地知道客戶數據庫的當前版本,以及須要在該數據庫上執行哪些數據庫更新腳本,來更新數據庫表結構與數據庫中已存在的數據。當升級完成時,數據庫表結構及數據應當與升級後的產品版本保持一致。
- 有的時候,咱們須要經過代碼(Java )來維護一些已存在的數據,如經過代碼來維護blob 類型的用戶頭像數據。
- 當升級失敗時(好比在升級過程當中出現網絡鏈接失敗),咱們應當支持對失敗進行修復。
3.2. Flyway 的特性
- 自動升級(自動發現更新項):Flyway 會將任意版本的數據庫升級到最新版本。Flyway 能夠脫離JVM 環境經過命令行執行,能夠經過Ant 腳本執行,經過Maven 腳本執行(這樣就能夠在集成環境自動執行),而且能夠在應用中執行(好比在應用啓動時執行)。
- 規約優於配置:Flyway 有一套默認的規約,因此不須要修改任何配置就能夠正常使用。
- 既支持SQL 腳本,又支持Java 代碼:可使用SQL 腳本執行數據庫更新,也可使用Java 代碼來進行一些高級數據升級操做。
- 高可靠性:在集羣環境下進行數據庫升級是安全可靠的。
- 支持清除已存在的庫表結構:Flyway 能夠清除已存在的庫表結構,能夠從零開始搭建您的庫表結構,並管理您的數據庫版本升級工做。
- 支持失敗修復。新的2.0 版本提供了repair 功能,用於解決數據庫更新操做失敗問題。
結合咱們遇到的問題,與Flyway 所提供的特性,咱們認爲Flyway 是比較適合於咱們的一款數據庫版本管理工具。 安全
4. 如何使用Flyway
使用Flyway ,咱們須要準備Flyway 將要執行的數據庫腳本(Flyway 支持sql 腳本與java 代碼,這裏認爲在Flyway下執行數據庫更新操做的java 代碼也是一種數據庫腳本),而後經過Flyway 提供的幾種不一樣運行方式來執行這些腳本。(如下配置參數說明基於Flyway 1.7 版本,新的2.0 版本在配置參數上有很多變更,與下面的介紹會有很多出入,如下說明僅供參考) 服務器
4.1. 數據庫腳本
Flyway 的主要任務是管理數據庫的版本更新,在Flyway 中稱每次數據庫更新爲一個migration ,爲了更順口,咱們下面稱之爲數據庫腳本。Flyway 支持SQL-based migrations 和Java-based migrations 。 網絡
Flyway 支持的數據庫腳本有sql 腳本與java 代碼,sql 腳本即普通的sql 腳本,包含建立數據庫、表,更新庫表結構,數據插入、更新、刪除等sql 語句,java 代碼則是經過一個有效的數據源,使用java 語言來進行數據庫的操做,這裏針對的讀者是對數據庫操做有必定熟悉程度的羣體,再也不詳細講解如何編寫數據庫腳本。
Flyway 的sql 腳本與java 代碼都遵循如下默認規約:
4.1.1. Flyway 默認規約
- SQL 腳本文件默認位置是項目的源文件夾下的db/migration 目錄。
- Java 代碼默認位於db.migration 包。
- SQL 腳本文件及Java 代碼類名必須遵循如下命名規則:V<version>[_<SEQ>][__description] 。版本號的數字間以小數點(. )或下劃線(_ )分隔開,版本號與描述間以連續的兩個下劃線(__ )分隔開。如V1_1_0__Update.sql 。Java 類名規約不容許存在小數點,因此Java 類名中版本號的數字間只能如下劃線進行分隔。
4.2. Flyway 的幾種運行方式
本章主要講解咱們經常使用的三種Flyway 的執行方式,Flyway 除了提供這三種執行方式外,還提供Ant 任務方式的執行方式,有興趣的同窗能夠去官方網站獲取相關信息,這裏不進行描述。
4.2.1. 命令行方式
經過命令行方式運行Flyway ,須要下載flyway-commandline 版本並解壓到本地,而後flyway (Windows下flyway.cmd ,Linux 下flyway.sh )命令執行Flyway 相關操做。
下圖是flyway-commandline-1.7 解壓後的目錄結構:
命令行方式的特色與規約
- 無需安裝JVM ,Maven ,Ant
- 默認讀取conf/flyway.properties 中的配置信息,若是在命令行中指定參數,命令行中指定的參數將覆蓋配置文件中的配置
- 還能夠經過參數-configFlie=myFlyway.properties 來從新指定flyway 配置文件,能夠經過-configFileEncoding=GBK 來指定配置文件的編碼格式
- 能夠將打包好的java 遷移文件放到jars/ 目錄下讓flyway 能夠找到並運行
- 數據庫驅動包(jar )放到jars/ 目錄下
- sql 腳本文件放到sql/ 目錄中
命令行方式運行的配置及使用方法
- 修改conf/flyway.properties 配置文件
- 拷貝數據庫jdbc 驅動jar 到jars/ 目錄
- 在sql/ 目錄下建立配置好的sql 腳本文件目錄路徑,如flyway 默認的sql 文件路徑爲db/migration ,咱們就須要在sql/ 目錄下建立/db/migration 目錄結構
- 將數據庫維護腳本放到建立好的sql 腳本文件目錄中(維護腳本文件名須要遵循命名規範)
- 在命令行執行命令(從flyway 安裝目錄開始執行)flyway init (初始化Flyway metadata )、flyway migrate(執行Flyway 升級操做)、flyway validate (校驗Flyway 數據正確性)
4.2.2. Maven 插件
配置Maven 插件
Xml代碼
- <plugin>
- <groupId>com.googlecode.flyway</groupId>
- <artifactId>flyway-maven-plugin</artifactId>
- <version>1.7</version>
- <dependencies>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>${mysql.connector.version}</version>
- </dependency>
- </dependencies>
- <configuration>
- <driver>com.mysql.jdbc.Driver</driver>
- <url>jdbc:mysql://localhost/flywaydemo?useUnicode=true&characterEncoding=utf-8</url>
- <user>root</user>
- <password></password>
-
- <!-- 設置接受flyway進行版本管理的數據庫,多個數據庫以逗號分隔 -->
- <schemas>flywaydemo</schemas>
- <!-- 設置存放flyway metadata數據的表名 -->
- <table>schema_version</table>
- <!-- 設置flyway掃描sql升級腳本、java升級腳本的目錄路徑或包路徑 -->
- <locations>
- <location>flyway/migrations</location>
- <location>com.kedacom.flywaydemo.migrations</location>
- </locations>
- <!-- 設置sql腳本文件的編碼 -->
- <encoding>UTF-8</encoding>
- <!-- 設置執行migrate操做以前的validation行爲 -->
- <validationMode>ALL</validationMode>
- <!-- 設置當validation失敗時的系統行爲 -->
- <validationErrorMode>FAIL</validationErrorMode>
- </configuration>
- </plugin>
上面的插件配置包含了幾方面的配置信息:
- 聲明插件
- 聲明數據庫驅動的依賴包
- Flyway 配置——數據庫鏈接配置
- Flyway 配置——Flyway 參數與行爲配置
執行Maven 命令進行Flyway 操做(下面列出幾種經常使用的操做)
- mvn flyway:init (初始化Flyway metadata )
- mvn flyway:migrate (執行Flyway 升級操做)
- mvn flyway:validate (校驗Flyway 數據正確性)
4.2.3. 在應用啓動時自動運行(結合Spring )
定義在應用啓動時自動運行Flyway 的Java 類,並實現其邏輯代碼
Java代碼
- public class FlywayMigration {
-
- private DataSource dataSource;
-
- public void setDataSource(DataSource dataSource) {
- this.dataSource = dataSource;
- }
-
- public void migrate() {
- Flyway flyway = new Flyway();
- flyway.setDataSource(dataSource);
-
- flyway.setSchemas("flywaydemo"); // 設置接受flyway進行版本管理的多個數據庫
- flyway.setTable("schema_version"); // 設置存放flyway metadata數據的表名
- flyway.setLocations("flyway/migrations", "com.kedacom.flywaydemo.migrations"); // 設置flyway掃描sql升級腳本、java升級腳本的目錄路徑或包路徑
- flyway.setEncoding("UTF-8"); // 設置sql腳本文件的編碼
- flyway.setValidationMode(ValidationMode.ALL); // 設置執行migrate操做以前的validation行爲
- flyway.setValidationErrorMode(ValidationErrorMode.FAIL); // 設置當validation失敗時的系統行爲
-
- flyway.migrate();
- }
-
- }
在Spring 中根據上面實現的類來定義(實例化)一個bean
Xml代碼
- <bean id="flywayMigration" class="com.kedacom.flywaydemo.FlywayMigration" init-method="migrate">
- <property name="dataSource" ref="dataSource" />
- </bean>
從上面的bean 定義中咱們能夠看到,咱們爲flywayMigration 這個bean 實例注入了一個數據源,Flyway 的全部操做將針對這個數據源進行;同時咱們經過init-method 屬性指定了Spring 在實例化該bean 之後,主動執行該bean的migrate 方法,而該方法內會執行Flyway 更新數據庫的操做。
至此,咱們達到了在應用啓動時,Spring 實例化上下文的時候,在Spring 實例化flywayMigration 這個bean 的時候,自動執行Flyway 更新數據庫的操做。
可是,咱們尚未達到目的,萬一Flyway 還在更新數據庫,沒有完成更新操做以前,應用程序的其餘邏輯已經開始使用數據庫進行其餘操做了,會致使應用程序產生不少bug ,甚至根本運行不起來。
要解決這個問題,咱們能夠利用Spring 的bean 依賴原理,讓關鍵的數據庫操做bean 依賴於flywayMigration 這個bean ,達到在flywayMigration 沒有實例化完成(數據庫更新操做完成)以前,不能進行任何其餘數據庫相關操做。
利用Spring 的bean 依賴讓flywayMigration 優先處理數據庫更新操做
Xml代碼
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" depends-on="flywayMigration">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" depends-on="flywayMigration">
- <property name="dataSource" ref="dataSource" />
- </bean>
5. 總結
本篇咱們介紹了什麼是Flyway ,爲何使用Flyway ,以及如何使用Flyway ,但實際產品/ 項目中的狀況可能更復雜,僅靠對Flyway 技術使用上的瞭解並不能達到咱們滿意的解決方案,爲此我將在下一篇中介紹咱們結合項目實際的問題造成的一些基於Flyway 的數據庫版本管理解決方案。下一篇的內容主要包括:
- 咱們的項目中實際是如何使用 Flyway 的
- 如何在已有的項目中集成 Flyway
- 如何在多應用、跨平臺、跨語言的環境中使用 Flyway