`
wxb880114
  • 浏览: 681777 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

快速掌握Flyway

    博客分类:
  • CI
阅读更多

快速掌握Flyway

什么是Flyway?

Flyway is an open-source database migration tool. It strongly favors simplicity and convention over configuration.

Flyway是一款开源的数据库版本管理工具,它更倾向于规约优于配置的方式。Flyway可以独立于应用实现管理并跟踪数据库变更,支持数据库版本自动升级,并且有一套默认的规约,不需要复杂的配置,Migrations可以写成SQL脚本,也可以写在Java代码中,不仅支持Command Line和Java API,还支持Build构建工具和Spring Boot等,同时在分布式环境下能够安全可靠地升级数据库,同时也支持失败恢复等。

Flyway主要基于6种基本命令:MigrateCleanInfoValidateBaseline and Repair,稍候会逐一分析讲解。目前支持的数据库主要有:Oracle, SQL Server, SQL Azure, DB2, DB2 z/OS, MySQL(including Amazon RDS), MariaDB, Google Cloud SQL, PostgreSQL(including Amazon RDS and Heroku), Redshift, Vertica, H2, Hsql, Derby, SQLite, SAP HANA, solidDB, Sybase ASE and Phoenix.

关于Flyway的优势,支持的数据库以及与其他数据库版本工具的对比,可以阅读Flyway官网介绍

为什么使用Flyway?

通常在项目开始时会针对数据库进行全局设计,但在开发产品新特性过程中,难免会遇到需要更新数据库Schema的情况,比如:添加新表,添加新字段和约束等,这种情况在实际项目中也经常发生。那么,当开发人员完成了对数据库更的SQL脚本后,如何快速地在其他开发者机器上同步?并且如何在测试服务器上快速同步?以及如何保证集成测试能够顺利执行并通过呢?

假设以Spring Boot技术栈项目为例,可能有人会说,本地使用Hibernate自动更新数据库Schema模式,然后让QA或DEV到测试服务器上手动执行SQL脚本,同时可以写一个Gradle任务自动执行更新。

个人觉得,对于Hibernate自动更新数据库,感觉不靠谱,不透明,控制自由度不高,而且有时很容易就会犯错,比如:用SQL创建的某个字段为VARCHAR类型,而在Entity中配置的为CHAR类型,那么在运行集成测试时,自动创建的数据库表中的字段为CHAR类型,而实际SQL脚本期望的是VARCHAR类型,虽然测试通过了,但不是期望的行为,并且在本地bootRun或服务器上运行Service时都会失败。另外,到各测试服务器上手动执行SQL脚本费时费神费力的,干嘛不自动化呢,当然,对于高级别和PROD环境,还是需要DBA手动执行的。最后,写一段自动化程序来自动执行更新,想法是很好的,那如果已经有了一些插件或库可以帮助你更好地实现这样的功能,为何不好好利用一下呢,当然,如果是为了学习目的,重复造轮子是无可厚非的。

其实,以上问题可以通过Flyway工具来解决,Flyway可以实现自动化的数据库版本管理,并且能够记录数据库版本更新记录,Flyway官网对Why database migrations结合示例进行了详细的阐述,有兴趣可以参阅一下。

Flyway如何工作的?

Flyway对数据库进行版本管理主要由Metadata表和6种命令完成,Metadata主要用于记录元数据,每种命令功能和解决的问题范围不一样,以下分别对metadata表和这些命令进行阐述,其中的示意图都来自Flyway的官方文档。

Metadata Table

Flyway中最核心的就是用于记录所有版本演化和状态的Metadata表,在Flyway首次启动时会创建默认名为SCHEMA_VERSION的元数据表,其表结构为(以MySQL为例):

Field Type Null Key Default
version_rank int(11) NO MUL NULL
installed_rank int(11) NO MUL NULL
version varchar(50) NO PRI NULL
description varchar(200) NO   NULL
type varchar(20) NO   NULL
script varchar(1000) NO   NULL
checksum int(11) YES   NULL
installed_by varchar(100) NO   NULL
installed_on timestamp NO   CURRENT_TIMESTAMP
execution_time int(11) NO   NULL
success tinyint(1) NO MUL NULL

Flyway官网上提供了一个很清晰的示例How Flyway works,可以参阅一下。

Migrate

Migrate是指把数据库Schema迁移到最新版本,是Flyway工作流的核心功能,Flyway在Migrate时会检查Metadata(元数据)表,如果不存在会创建Metadata表,Metadata表主要用于记录版本变更历史以及Checksum之类的。

Migrate时会扫描指定文件系统或Classpath下的Migrations(可以理解为数据库的版本脚本),并且会逐一比对Metadata表中的已存在的版本记录,如果有未应用的Migrations,Flyway会获取这些Migrations并按次序Apply到数据库中,否则不需要做任何事情。另外,通常在应用程序启动时应默认执行Migrate操作,从而避免程序和数据库的不一致性。

Clean

Clean相对比较容易理解,即清除掉对应数据库Schema中的所有对象,包括表结构,视图,存储过程,函数以及所有的数据等都会被清除。

Clean操作在开发和测试阶段是非常有用的,它能够帮助快速有效地更新和重新生成数据库表结构,但特别注意的是:不应在Production的数据库上使用!

Info

Info用于打印所有Migrations的详细和状态信息,其实也是通过Metadata表和Migrations完成的,下图很好地示意了Info打印出来的信息。

Info能够帮助快速定位当前的数据库版本,以及查看执行成功和失败的Migrations。

Validate

Validate是指验证已经Apply的Migrations是否有变更,Flyway是默认是开启验证的。

Validate原理是对比Metadata表与本地Migrations的Checksum值,如果值相同则验证通过,否则验证失败,从而可以防止对已经Apply到数据库的本地Migrations的无意修改。

Baseline

Baseline针对已经存在Schema结构的数据库的一种解决方案,即实现在非空数据库中新建Metadata表,并把Migrations应用到该数据库。

Baseline可以应用到特定的版本,这样在已有表结构的数据库中也可以实现添加Metadata表,从而利用Flyway进行新Migrations的管理了。

Repair

Repair操作能够修复Metadata表,该操作在Metadata表出现错误时是非常有用的。

Repair会修复Metadata表的错误,通常有两种用途:

  • 移除失败的Migration记录,该问题只是针对不支持DDL事务的数据库。
  • 重新调整已经应用的Migratons的Checksums值,比如:某个Migratinon已经被应用,但本地进行了修改,又期望重新应用并调整Checksum值,不过尽量不要这样操作,否则可能造成其它环境失败。

如何使用Flyway?

这里将主要关注在Gradle和Spring Boot中集成并使用Flyway,数据库通常会采用MySQL、PostgreSQL、H2或Hsql等。

正确创建Migrations

Migrations是指Flyway在更新数据库时是使用的版本脚本,比如:一个基于Sql的Migration命名为V1__init_tables.sql,内容即是创建所有表的sql语句,另外,Flyway也支持基于Java的Migration。Flyway加载Migrations的默认Locations为classpath:db/migration,也可以指定filesystem:/project/folder,其加载是在Runtime自动递归地执行的。

除了需要指定Location外,Flyway对Migrations的扫描还必须遵从一定的命名模式,Migration主要分为两类:Versioned和Repeatable。

  • Versioned migrations
    一般常用的是Versioned类型,用于版本升级,每一个版本都有一个唯一的标识并且只能被应用一次,并且不能再修改已经加载过的Migrations,因为Metadata表会记录其Checksum值。其中的version标识版本号,由一个或多个数字构成,数字之间的分隔符可以采用点或下划线,在运行时下划线其实也是被替换成点了,每一部分的前导零会被自动忽略。

  • Repeatable migrations
    Repeatable是指可重复加载的Migrations,其每一次的更新会影响Checksum值,然后都会被重新加载,并不用于版本升级。对于管理不稳定的数据库对象的更新时非常有用。Repeatable的Migrations总是在Versioned之后按顺序执行,但开发者必须自己维护脚本并且确保可以重复执行,通常会在sql语句中使用CREATE OR REPLACE来保证可重复执行。

默认情况下基于Sql的Migration文件的命令规则如下图所示:

其中的文件名由以下部分组成,除了使用默认配置外,某些部分还可自定义规则。

  • prefix: 可配置,前缀标识,默认值V表示Versioned,R表示Repeatable
  • version: 标识版本号,由一个或多个数字构成,数字之间的分隔符可用点.或下划线_
  • separator: 可配置,用于分隔版本标识与描述信息,默认为两个下划线__
  • description: 描述信息,文字之间可以用下划线或空格分隔
  • suffix: 可配置,后续标识,默认为.sql

另外,关于如何使用基于Java的Migrations,有兴趣可以参考Java-based migrations

支持的数据库

目前Flyway支持的数据库还是挺多的,包括:Oracle, SQL Server, SQL Azure, DB2, DB2 z/OS, MySQL(including Amazon RDS), MariaDB, Google Cloud SQL, PostgreSQL(including Amazon RDS and Heroku), Redshift, Vertica, H2, Hsql, Derby, SQLite, SAP HANA, solidDB, Sybase ASE and Phoenix。
目前来说,个人用得比较多的数据库是PostgreSQLMySQLH2Hsql,针对每种数据库的flyway.url示例配置为:

1
2
3
4
5
6
7
8
9
10
11
# PostgreSQL
flyway.url = jdbc:postgresql://localhost:5432/postgres?currentSchema=myschema

# MySQL
flyway.url = jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC&useSSL=true

# H2
flyway.url = jdbc:h2:./.tmp/testdb

# Hsql
flyway.url = jdbc:hsqldb:hsql//localhost:1476/testdb

 

Flyway命令行

Flyway的命令行工具支持直接在命令行中运行MigrateCleanInfoValidateBaselineRepair6种命令,不需要借助其他Build工具,不需要应用程序运行在JVM中,只需要单纯的命令行即可,但需要根据不同的操作系统下载并安装该命令行工具。Flyway会依次搜索以下配置文件,越靠后的配置会覆盖靠前的配置:

  • /conf/flyway.conf
  • /flyway.conf
  • /flyway.conf

一个典型Flyway项目示例目录结构如下:

更多关于Flyway命令行使用可以参考Flyway Command-line

在Gradle中的应用

首先需要在Gradle中引入Flyway插件,通常有两种方式:

  • 方式一:采用buildscript依赖方式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.flywaydb:flyway-gradle-plugin:4.0.3")
        }
    }
    apply plugin: 'org.flywaydb.flyway'
    
  • 方式二(推荐):采用DSL方式引用Plugins。

    1
    2
    3
    
    plugins {
        id "org.flywaydb.flyway" version "4.0.3"
    }
    

而在Gradle中配置Flyway Properties有两种方式:

  • 方式一:在build.gradle中配置Flyway Properties。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    flyway {
    	url = jdbc:h2:./.tmp/testdb
    	user = sa
    	password = 
    }
    
    # 或者写成:
    project.ext['flyway.url'] = 'jdbc:h2:./.tmp/testdb'
    project.ext['flyway.user'] = 'sa'
    project.ext['flyway.password'] = ''
    
  • 方式二:在gradle.properties中配置Flyway Properties。

    1
    2
    3
    
    flyway.url = jdbc:h2:./.tmp/testdb
    flyway.user = sa
    flyway.password =
    

如果期望在运行Gradle Clean/Build Tasks时自动执行Flyway的某些任务,可以设置dependsOn,若不期望隐式执行Flyway任务,可以不配置。

1
2
clean.dependsOn flywayRepair  # To repair the Flyway metadata table
build.dependsOn flywayMigrate  # To migrate the schema to the latest version

 

另外,其它Tasks:flywayInfoflywayValidateflywayBaseline分别对应到Flyway的命令。在使用Spring Boot时,运行./gradlew bootRun会自动检查并加载最新的db.migration脚本。

特别注意:在Production环境中不应执行./gradlew flywayClean,除非你知道自己的行为和目的,因为该命令会清除所有的数据库对象,相当危险。

更多关于Flyway在Gradle中的使用请参阅Flyway Gradle Plugin

与Spring Boot集成

在Spring Boot中,如果加入Flyway的依赖,则会自动引用Flyway并使用默认值,但可以修改并配置FlywayProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
flyway.baseline-description= # The description to tag an existing schema with when executing baseline.
flyway.baseline-version=1 # Version to start migration.
flyway.baseline-on-migrate=false # Whether to execute migration against a non-empty schema with no metadata table
flyway.check-location=false # Check that migration scripts location exists.
flyway.clean-on-validation-error=false # will clean all objects. Warning! Do NOT enable in production!
flyway.enabled=true # Enable flyway.
flyway.encoding=UTF-8 # The encoding of migrations.
flyway.ignore-failed-future-migration=true # Ignore future migrations when reading the metadata table.
flyway.init-sqls= # SQL statements to execute to initialize a connection immediately after obtaining it.
flyway.locations=classpath:db/migration # locations of migrations scripts.
flyway.out-of-order=false # Allows migrations to be run "out of order".
flyway.placeholder-prefix=  # The prefix of every placeholder.
flyway.placeholder-replacement=true # Whether placeholders should be replaced.
flyway.placeholder-suffix=} # The suffix of every placeholder.
flyway.placeholders.*= # Placeholders to replace in Sql migrations.
flyway.schemas= # Default schema of the connection and updating
flyway.sql-migration-prefix=V # The file name prefix for Sql migrations
flyway.sql-migration-separator=__ # The file name separator for Sql migrations
flyway.sql-migration-suffix=.sql # The file name suffix for Sql migrations
flyway.table=schema_version # The name of Flyway's metadata table.
flyway.url= # JDBC url of the database to migrate. If not set, the primary configured data source is used.
flyway.user= # Login user of the database to migrate. If not set, use spring.datasource.username value.
flyway.password= # JDBC password if you want Flyway to create its own DataSource.
flyway.validate-on-migrate=true # Validate sql migration CRC32 checksum in classpath.

 

若使用Gradle,通常在build.gradle引入org.flywaydb:flyway-core:4.0.3依赖后即可使用。可能会有以下几种需求:

  • 在本地Run和Tests都会使用内存数据库,其中的spring.jpa.hibernate.ddl-auto都设置为validate,Schema不需要Hibernate自动生成,并期望使用Flyway,而在线上环境会使用真实数据库,并不期望使用Flyway,如何实现呢?
    解决方案:可以在common.properties中配置flyway.enabled=false,然后在local或dev的配置中启用Flyway即可。通常推荐使用此模式,毕竟可以对不同的环境进行控制,另外本地Run不会依赖真实数据库,又能保证数据库Schema是按脚本创建的。

  • 在运行Tests会使用内存数据库,有单独的配置文件,不使用Flyway,而在本地bootRun时会使用真实数据库,使用Flyway,毕竟不想每次Schema改后都在本地手动去执行脚本,如何实现?
    解决方案:设置bootRun.dependsOn动态添加Flyway的依赖即可:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    addFlywayDenpendency {
    	doLast {
    		dependencies {
    			compile('org.flywaydb:flyway-core:4.0.3')
    		}
    	}
    }
    
    bootRun.dependsOn=addFlywayDenpendency
    
  • 若项目有多个团队同时开发不同的功能,需要新建多个分支,并且都会涉及到数据库Schema更改,当后期Merge时,Migration的版本如何控制并且不会产生数据库更改的冲突呢?
    解决方案:如果两个分支的数据库更改有冲突,要么最初数据库设计不合理,要么目前数据库更改不合理,所以需要团队进行全局考虑和协调。而针对数据库在同一段时间有修改,但不会造成冲突的情况,通常实际项目中主要存在这样的情况,那可以设置flyway.out-of-order=true,这样允许当v1和v3已经被应用后,v2出现时同样也可以被应用。其实在本地使用内存数据库不会存在该问题,因为数据库所有对象会自动清除掉,而在local或dev中使用真实数据库时可遇到这样的问题,因此需要注意一下了。
    另外,值得一提的是Flyway的参数ignore-failed-future-migration默认为true,使用情形为:当Rollback数据库更改到旧版本,而metadata表中已存在了新版本时,Flyway会忽略此错误,只会显示警告信息。

结束语

总得来说,Flyway可以有效改善数据库版本管理方式,如果项目中还未使用,不防尝试一下。如果有兴趣,也可以关注MyBatis Migration,功能支持没有Flyway多,属于更轻量级的数据库版本管理工具。如果在使用过程中遇到了问题或坑,欢迎留言一起交流讨论。


References

http://blog.waterstrong.me/flyway-in-practice/

分享到:
评论

相关推荐

    flyway简单测试工程

    【Flyway:数据库版本管理利器】 Flyway是一款强大的开源数据库版本管理工具,它使得数据库的迁移变得简单且有序。...在实际项目中,结合标签“flyway demo”,我们可以参考此工程,快速上手并熟练掌握Flyway的使用。

    SpringBoot快速入门3434

    ### Spring Boot 应用程序配置详解 #### 一、引言 随着软件开发技术的不断发展,框架和工具的出现极大地简化了开发流程。...希望本文介绍的配置属性能帮助您更好地理解和掌握 Spring Boot 的配置机制。

    暴露于spring-kotlin的场所:用于Spring启动2. *的游乐场,暴露于kotlin,暴露于jetbrains的,postgres,jsonb,flyway,docker

    Spring Boot是Java生态系统中的一个流行微服务框架,它简化了配置并提供了快速开发应用的方式。Kotlin是一种现代、类型安全且适用于多平台的编程语言,它在Java虚拟机(JVM)上运行,且被广泛用于Spring Boot应用。 ...

    Spring-Boot-Reference-Guide, Spring Boot Reference Guide中文翻译 -《Spring Boot参考指南》.zip

    综上,《Spring Boot参考指南》覆盖了Spring Boot的各个方面,无论是初学者还是经验丰富的开发者,都能从中受益匪浅,快速掌握Spring Boot的使用技巧和最佳实践。这份中文翻译版本的出现,无疑为国内的Spring Boot...

    Spring MVC注解项目实例

    在本项目实例中,我们探讨的是如何使用Spring MVC框架结合注解进行开发,以及如何实现对数据库表的CRUD(创建、读取、更新、删除)操作...它为初学者提供了一个动手实践的机会,帮助他们快速掌握Spring MVC的开发技巧。

    springboot-crud.rar

    SpringBoot是一个由Pivotal团队开发的框架,它旨在...这个"springboot-crud"实例是学习SpringBoot和数据库操作的绝佳起点,通过实践这些知识点,可以快速掌握SpringBoot的基本用法,为后续的Web应用开发打下坚实基础。

    canopus

    Spring Boot的核心特点在于其自动配置,开发者无需编写大量繁琐的配置代码,即可快速构建出功能完备的应用。它内置了Tomcat服务器,使得应用可以直接运行,且支持通过jar包方式部署,这极大地方便了开发和运维工作。...

    项目创建者

    《项目创建者:基于Java的高效开发工具》 在软件开发领域,项目创建是初始阶段...通过理解和掌握这些技术,开发者不仅能有效地创建新项目,还能在已有项目的基础上进行迭代和升级,实现软件开发的无缝对接和持续优化。

    boot-app:此存储库是Spring Boot和Angular2教程的示例应用程序

    Spring Boot是基于Spring框架的快速开发工具,它内置了Tomcat服务器,提供了“开箱即用”的功能,如自动配置、内嵌数据库支持等。开发者不再需要编写大量的XML配置文件,而是可以利用注解驱动的方式轻松管理应用的...

    基于SpringBoot2.0的后台权限管理系统界面简洁美观敏捷开发系统架构

    - 快速迭代:SpringBoot的快速启动和热部署特性,有利于快速开发和迭代。 - 模块化设计:系统采用模块化设计,便于团队协作和代码复用。 - 测试支持:SpringBoot内置了测试支持,方便进行单元测试和集成测试。 7...

    SpringBoot深入实践pdf及源码

    2. **起步驱动配置**:讲解SpringBoot如何通过起步依赖(Starters)简化项目构建,以及自动配置机制如何工作,让应用程序能够快速启动运行。 3. **Web开发**:详细阐述如何使用SpringBoot进行Web开发,包括RESTful ...

    Spring Boot核心技术-笔记-pdf版.pdf

    **Spring Boot核心技术详解** Spring Boot是由Pivotal团队提供的全新框架,其设计目标是为了简化Spring应用的...希望这份学习笔记能帮助你深入理解和掌握Spring Boot的精髓,祝你在Spring Boot的学习之路上一帆风顺!

    spring boot相关书籍

    1. **快速开始**:引导读者快速建立一个Spring Boot应用,理解其基本结构。 2. **特性**:详细解释Spring Boot的主要特性,如内嵌Servlet容器、起步依赖、自动配置等。 3. **Web应用**:介绍Spring MVC的使用,...

    官网自动化及性能

    在实际操作中,持续集成/持续部署(CI/CD)工具,如Jenkins、GitLab CI/CD或Travis CI,可以自动化构建、测试和部署流程,确保快速反馈和稳定发布。 关于“源码”,源码是软件开发的基础,理解并掌握官网的源代码能...

    SpringBoot实战_springboot实战_

    通过《SpringBoot实战》的学习,读者不仅可以掌握SpringBoot的基本用法,还能了解到如何在实际项目中充分利用其特性,提高开发效率,构建稳定、高效的业务系统。书中的实例和代码示例将帮助读者更好地理解和应用这些...

    读取数据库表结构生成自定义代码.zip

    自动化工具如Flyway、Liquibase等可以用来管理数据库迁移,它们也能根据表结构生成升级脚本。 最后,标签“数据结构”在这里可能指的是数据库内部如何存储和组织数据。不同的数据结构(如堆、索引、B树、哈希表等)...

    银行复杂系统环境下的 数据库DevOps实践.zip

    4. **数据迁移工具**:如Liquibase、Flyway等,用于管理和追踪数据库的结构变化,简化数据库升级过程。 5. **数据虚拟化**:在开发和测试阶段,使用数据虚拟化技术生成模拟数据,保护真实数据隐私,同时满足测试...

    自动化测试环境部署相关文件

    使用如Flyway、 Liquibase等数据库迁移工具可以帮助管理数据库结构的变化,而Mockito等工具可用于模拟数据交互,避免对真实数据库的直接操作。 综上所述,自动化测试环境部署涉及多个方面,从环境配置、工具选择到...

    spring-boot2.0-中文参考指南

    这个中文参考指南是开发者掌握Spring Boot 2.0核心特性和最佳实践的重要资源。 Spring Boot的核心理念在于“约定优于配置”,通过内置的默认设置,使得开发者能够快速启动项目,无需编写大量繁琐的配置代码。在2.0...

Global site tag (gtag.js) - Google Analytics