Flyway做为database migration开源工具,功能上像是git、svn这种代码版本控制。google搜索database migration,或者针对性更强些搜索database migration java,会有其它的framework、tool、甚至aws的service。因为项目使用到了flyway,而且确实google中排第一位的搜索结果就是flyway,那就之后有机会再与其它方案做比较,先对flyway做下学习笔记。
0.支持的数据库
支持数据库有 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。
1. 安装
1.1 Cli
从官网下载压缩包:https://flywaydb.org/documentation/commandline/。
解压后cd到目录下,就可以直接执行flyway。解压后的文件夹结构如下图:
如果操作数据库,需要先在/conf/flyway.conf文件中做下配置,主要是对flyway.url、flyway.user和flyway.password(如何支持针对多个database,尚未做实验,待论),注释中给出了url的详细格式,可以根据环境自行添加。
1.2 Spring Integrating
dataSource中已经配置了数据库的driver、与flyway.conf中flyway.url格式相同的数据库信息,同时还有username和password,相当于这里就是为flyway配置conf文件
2 |
< bean id = "flyway" class = "org.Flyway.core.Flyway" init-method = "migrate" depends-on="dataSource" > |
3 |
< property name = "dataSource" ref = "dataSource" /> |
4 |
< property name = "locations" value = "postgresql/db/migrations/" /> |
5 |
</ bean > |
每次app启动的时候,确切说在flyway的bean初始化的时候,就会自动检测扫描locations路径下的文件。locations文件夹中的文件,按照flyway官方默认的命名方式,就是用Vx_x__description.sql。sql文件,还有实现JdbcMigration接口生成的class文件(后文描述),都会列在schema_version表中,作为版本控制的原子项。比如在部署环境,只要war包中locations中的sql文件改变了,那么数据库就会做出相应的调整。
2. 配置
2.1 flyway.conf
flyway.conf:加载配置文件遵循下列顺序,并且后面加载的配置会覆盖前面的配置。
· <install-dir>/conf/flyway.conf
· <user-home>/flyway.conf
· <current-dir>/flyway.conf
文件中除了配置数据库信息,还能对比如prefix、separator、suffix做定义,默认prefix是V(执行一遍,并且版本号唯一,如果有重复就会报错)或R(重复执行,不需要版本号),separator是双下划线__,suffix是.sql。
官方文档中给出了SQL migration执行过程中扫描文件的顺序,即classpath:db/migration或者file system: 自定义文件夹。或者在执行flyway migration的时候使用-X参数,就能看到执行流程,这个确实有助于理解flyway的workflow。
2.2 drivers
flyway使用JDBC连接数据库,在drivers文件夹下有默认的几种,
· SQL Server (jTDS)
· MySQL
· MariaDB
· PostgreSQL
· Redshift
· H2
· Hsql
· Derby
· SQLite
如果要操作其它数据库,需要手动添加JDBC的driver到此文件夹下。
如果是用Spring集成,那么需要特别指定用的drivers是哪一个,比如org.postgresql.Driver
3. 基本指令
一共就6个基本指令:migrate、clean、info、validate、baseline、repair。强烈推荐在使用初期使用-X参数,log会显示flyway自己的业务逻辑。
4. 处理逻辑
4.1 schema_version
Flyway会生成一个表schema_version来记录文件的执行情况,关于生成与修改这个表的相关文件,在lib文件夹中有一个jar包flyway-core-2.1.1.jar,可以用WinZip打开,在下图位置示例了生成表的createMetaDataTable.sql文件,以及操作相关的class文件。
schema_version表中记录的每个脚本文件的执行情况,官方文档上有pending、success、failed、outdated和future,我还没有出现failed和outdated,不过有ignored,可能是因为用了V0。
4.2 文件处理逻辑
做为测试,我使用的执行步骤如下:
1. V1__create放入文件夹,migrate成功
2. V0__drop放入文件夹,migrate,结果是ignored
3. V0改为V1,numeric冲突,报错了
4. V0__drop改为V1_1__drop,migrate,执行成功
5. V1_1_drop删除,migrate,在schema_version表中,V1_1_drop状态为future,说明表中不会自己删除那些文件已不存在的item,而且drop掉的表不会再被创建,因为在schema_version表中V1__create的状态已经是success了。
5. FAQ
其中几个为官方文档的翻译,里面有些问题虽然没碰到,不过先自己翻译一遍加深理解。
原文链接:https://flywaydb.org/documentation/faq
对于降级操作(downgrade scripts/downward migrations)的支持
Flyway does NOT support downgrade scripts.
While the idea of downgrade scripts (popularized by Rails Migrations) is a nice one in theory, unfortunately it breaks down in practice. As soon as you have destructive changes (drop, delete, truncate, ...), you start getting into trouble. And even if you don‘t, you end up creating home-made alternatives for restoring backups, which need to be properly tested as well.
Flyway不支持降级的脚本:
一旦执行了诸如drop、delete、truncate这样的清除性动作,从之前的实验操作结果,我理解并不是说flyway不会甚至不能执行降级操作,毕竟drop、delete、truncate只是一些指令,flyway本身并没有能力看出来是哪些操作,这个回答应该是想说,最好不要做诸如此类的清除性动作,否则麻烦就来了,除非之前已经做了充分的备份以恢复数据。
Downgrade scripts assume the whole migration failed.
A migration can fail at any point. If you have 10 statements, it is possible for the 1st, the 5th, the 7th or the 10th to fail. There is simply no way to know in advance. Downgrade scripts are written to roll back an entire migration. This renders them effectively useless, even for non-destructive changes.
Migration执行失败可能发生在任何一个脚本,而降级操作相当于是在之前已经完成的基础上,那么如果前面的脚本执行失败,那么后面的操作就不会起作用,即便不是消除性操作。
我理解答案是想说明migration执行成功与否具有不确定性,而且版本号越高不确定性越强,因为会依赖先前的执行结果。
Maintain backwards compatibility between the DB and all versions of the code currently deployed in production.
This way a failed migration is not a disaster. The old version of the application is still compatible with the DB, so you can simply roll back the application code, investigate, and take corrective measures.
要确保数据库与线上各个版本代码的兼容性,才不会因为一次失败的migration脚本而造成重大损失。而且当真正执行失败的时候,也可以恢复回老版本的代码,再对错误做出修正。
A much better solution is a proper, well tested, backup and restore strategy.
It is independent of the database structure, and once it is tested and proven to work, no migration script can break it. For optimal performance, and if your infrastructure supports this, we recommend using the snapshot technology of your underlying storage solution. Especially for larger data volumes, this can be several orders of magnitude faster than traditional backups and restores!
这里是推荐在条件允许的情况下,用快照对已上线的系统做数据备份,说是比传统的数据备份与恢复要快。
如何做好热修复?
比如说现在已经上线的是版本7,正在开发的是版本8,而且版本8在DB schema上有些改动。此时有些bug导致需要对版本7做些热修复,也涉及到了schema的改动。
一般来说,虽然代码有多个分支,不过schema不会。所以可以将热修复的脚本标号为7.1,并且适用于修复版本7,和新的版本8。这样在版本8真正上线的时候,也会按照7、7.1、8的顺序执行脚本。
如果上述方案不行,还可以用outOfOrder(在flyway.conf有这个参数)属性,这样就可以允许不全部按照从小到大的顺序执行migration,也就是比schema_version中记录的最大值小的脚本,也能够被执行。
多个节点能够并行执行migration吗?
当然可以!Flyway使用数据库锁机制(locking technology of your database)来协调多个节点,从而保证多套应用程序可同时执行migration,而且集群控制也可做配置。
如果migration执行失败,会有回滚吗?
Flyway在不同的事物中执行migration,如果失败,那么事物就会回滚。不过不幸的是,现在只有DB2、PostgreSQL、Derby还有个别SQL Server扩展支持事物内的DDL。Other databases such as Oracle will implicitly sneak in a commit before and after each DDL statement, drastically reducing the effectiveness of this roll back. (没看太懂得一句)。一个可行的办法就是一个migration只包含单个DDL,只不过这样做比较累。
Flyway是否支持多schema?
当然支持!以下有几种策略来处理多schema的情况:
如果有多个相异的schema,可以用flyway的loop并且设置与schema对应的flyway.schemas。
如果schema的生命周期相同,那么可以只用一个flyway实例,设置flyway.schemas值为所有schemas以逗号隔开的字符串。所有的schema将只用到一个schema_version来记录脚本执行情况。(不知道此时schema_version表中会不会有专门的一列显示schema的名称)
如果schemas的生命周期不同,那就要启动多个flyway实例,每个实例处理自己的schema和metedata table,注意要把脚本分开放。