flyway实战
Flyway是一款开源的数据库版本管理工具,Flyway可以独立于应用实现管理并跟踪数据库的变更。
微服务架构下,随着微服务越来越多,如果采用传统的数据库版本管理方案,工作量巨大,且容易出错。
采用Flyway后,运维人员不再需要关注代码与数据库脚本的配套关系了,并且不需要手动执行升级脚本,无疑将大大减少工作量。
使用Flyway,对数据库的所有更改都称为迁移(migrations)。迁移可以是版本控制的,也可以是可重复的。
版本化迁移有两种形式:常规迁移和撤消迁移。
版本化迁移具有版本、描述和校验和(checksum)。版本必须是唯一的。该描述纯粹是为了让您能够记住每个迁移所做的事情。校验和用于检测意外更改。版本化迁移是最常见的迁移类型。它们被精确地应用一次。
或者,可以通过提供具有相同版本的撤销迁移来撤消它们的效果。
可重复迁移具有描述和校验和,但没有版本。它们不是只运行一次,而是在每次校验和更改时(重新)应用。
在一次迁移运行中,在执行了所有挂起的版本化迁移之后,总是最后应用可重复迁移。可重复迁移按其描述的顺序应用。
默认情况下,版本迁移和可重复迁移都可以用SQL或Java编写,并且可以由多条语句组成。
Flyway自动发现文件系统和Java类路径上的迁移。
为了跟踪哪些迁移已经在何时由谁应用,Flyway在数据库中添加一个schema历史表(flyway_schema_history)。
最常见的迁移类型是版本迁移。 每个版本迁移都有一个版本、一个描述和一个校验和。 版本必须是唯一的。 该描述纯粹是为了让您能够记住每个迁移所做的事情。 校验和用于检测意外更改。每个版本的sql是确定的,并且确定后不允许修改,通过校验和(checksum)即可验证sql是否被修改。 而对于可重复执行的迁移SQL,如果检测到校验和与记录的不一致,该SQL会重新执行。 版本化迁移只应用一次;可重复迁移,如果有修改会重新执行。
版本迁移通常用于:
例如:
CREATE TABLE car (
id INT NOT NULL PRIMARY KEY,
license_plate VARCHAR NOT NULL,
color VARCHAR NOT NULL
);
ALTER TABLE owner ADD driver_license_id VARCHAR;
INSERT INTO brand (name) VALUES ('DeLorean');
每个版本迁移必须分配一个唯一的版本号。
任何用点分隔的数字都是合法的版本号。在大多数情况下,一个简单的递增整数就足够了。
然而Flyway是非常灵活的,下面所有这些版本都是有效的迁移版本:
1
001
5.2
1.2.3.4.5.6.7.8.9
205.68
20130115113556
2013.1.15.11.35.56
2013.01.15.11.35.56
版本化的迁移SQL按其版本的顺序执行,并且只会执行一次。
撤销迁移与常规版本迁移相反。撤销迁移负责用相同的版本撤销版本迁移的影响。撤消迁移是可选的,不需要运行常规版本迁移。 对于上面的例子,撤销迁移是这样的:
DELETE FROM brand WHERE name='DeLorean';
ALTER TABLE owner DROP driver_license_id;
DROP TABLE car;
虽然撤销迁移的想法很好,但不幸的是,它在实践中有时会失败。
一旦发生了破坏性的更改(删除、删除、截断、…),就会开始遇到麻烦。
即使不这样做,最终也会创建用于恢复备份的自制替代方案,这些方案也需要经过适当的测试。
撤销迁移假定整个迁移成功了,现在应该撤销。这无助于在没有DDL事务的数据库上进行失败的版本迁移。
为什么呢?
迁移在任何一个点都可能失败。如果你的SQL文件中有10个SQL语句,第1个、第5个、第7个或第10个陈述都有可能失败。这根本没有办法提前知道。
相反,写撤销迁移是为了撤销整个版本迁移,在这种情况下不会有帮助。
我们发现另一种更好的方法是在DB和当前部署在生产环境中的代码的所有版本之间保持向后兼容性。这样,迁移失败就不是灾难。
旧版本的应用程序仍然与DB兼容,因此您可以简单地回滚应用程序代码、进行研究并采取纠正措施。
这应该辅之以适当的、经过良好测试的备份和恢复策略。
它独立于数据库结构,并且一旦经过测试并证明可以工作,任何迁移脚本都不能破坏它。
为了获得最佳性能,如果您的基础设施支持这一点,我们建议使用底层存储解决方案的快照技术。
特别是对于较大的数据量,这可能比传统的备份和恢复快几个数量级。
可重复迁移具有描述和校验和,但没有版本。 它们不是只运行一次,而是在每次校验和更改时(重新)运行。 这对于管理数据库对象非常有用,这些对象的定义可以简单地在版本控制中的单个文件中维护。 它们通常用于:
在一次迁移运行中,在执行了所有的版本化迁移之后,总是最后应用可重复迁移。
可重复迁移按其描述的顺序执行。
你需要确保可重复迁移的SQL可以多次执行,而不出现错误。
这通常涉及在DDL语句中使用CREATE或REPLACE子句。
下面是一个可重复迁移的例子:
CREATE OR REPLACE VIEW blue_cars AS
SELECT id, license_plate FROM cars WHERE color='blue';
基于SQL的迁移是最常用的迁移方式。这使中方式很直观、容易理解,并且很容易利用现有的SQL脚本工具进行调试。
直接使用SQL脚本实现数据库版本迁移,消除了理解任何中间转换层的需要。
基于SQL的迁移通常用于:
Flyway的SQL文件命名必须遵循如下模式: SQL文件名由以下部分组成:
Flyway可以从文件系统与Java类路径下查找SQL的迁移脚本。
查找路径默认为“classpath:db/migration”,它是可以配置的(配置项为locations,可以配置多个路径)。
在运行时通过文件系统和Java类路径扫描自动发现新的基于sql的迁移。
一旦您配置了想要使用的位置,Flyway将自动获取任何新的SQL迁移,只要它们符合配置的命名约定。
这种扫描是递归的。
指定目录下的非隐藏目录中的所有迁移也将被获取。
Flyway支持所有常规SQL语法元素,包括:
此外,对于Oracle, Flyway还支持SQL*Plus命令。
除了常规的SQL语法,Flyway还支持用可配置的前缀和后缀替换占位符。
默认情况下,它寻找ant风格的占位符,比如${myplaceholder}。
这对于抽象环境之间的差异非常有用。
例如:
/* 单行注释 */
CREATE TABLE test_user (
name VARCHAR(25) NOT NULL,
PRIMARY KEY(name)
);
/*
多行
注释
*/
-- 占位符(Placeholder)
INSERT INTO ${tableName} (name) VALUES ('Mr. T');
基于java的迁移非常适合所有不能使用SQL轻松解决的数据库版本迁移。
比如:
为了能够通让Flyway识别到,基于java的迁移必须实现JavaMigration接口。
然而,通常情况只需要从BaseJavaMigration继承就可以了,因为它采用Flyway的默认命名约定,允许Flyway自动从类名中提取版本和描述。
要做到这一点,类名必须遵循以下命名模式:
文件名由以下部分组成:
当然,你也可以按照自己的规则来命名Java类,这样的话,版本、描述、迁移类别则通过你的类实现的JavaMigration的方法来提供。
Flyway在locations属性引用的包中的Java类路径中发现基于Java的迁移。
在运行时通过类路径扫描自动发现新的java迁移。
扫描是递归的。
指定包的子包中的Java迁移也会被获取。
与SQL迁移不同,Java迁移在默认情况下没有校验和,因此不参与Flyway验证的更改检测。
可以通过实现getChecksum()方法来弥补这一点,然后可以使用该方法提供您自己的校验和,然后将对更改进行存储和验证。
package db.migration;
import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;
import java.sql.PreparedStatement;
/**
* Example of a Java-based migration.
*/
public class V1_2__Another_user extends BaseJavaMigration {
public void migrate(Context context) throws Exception {
PreparedStatement statement =
context.getConnection().prepareStatement("INSERT INTO test_user (name) VALUES ('Obelix')");
try {
statement.execute();
} finally {
statement.close();
}
}
}
如果您的应用程序已经使用了Spring,并且您不想直接使用JDBC,那么您可以轻松地使用Spring JDBC的JdbcTemplate:
package db.migration;
import org.flywaydb.core.api.migration.BaseJavaMigration;
import org.flywaydb.core.api.migration.Context;
import java.sql.PreparedStatement;
/**
* Example of a Java-based migration using Spring JDBC.
*/
public class V1_2__Another_user extends BaseJavaMigration {
public void migrate(Context context) {
new JdbcTemplate(new SingleConnectionDataSource(context.getConnection(), true))
.execute("INSERT INTO test_user (name) VALUES ('Obelix')");
}
}
默认情况下,Flyway总是为每个版本创建一个事务。 你也可以通过修改默认配置 group=true 来设置,所有的迁移在一个事务中执行。
如果Flyway检测到由于数据库的技术限制,某个特定语句无法在事务中运行,那么它就不会在事务中运行该迁移。 相反,它将被标记为非事务性的。
默认情况下,事务和非事务语句不能在迁移运行中混合。 但是,您可以通过将mix属性设置为true来实现这一点。
重要提示
如果数据库支持DDL语句事务,失败的迁移将总是回滚(除非它们被标记为非事务性的)。
如果另一方面数据库地支持事务中存在DDL语句(例如mysql中,当事务中遇到DDL语句,会将之前的执行进行隐式提交),因此如果后面出错了,将迁移标记为失败,表明可能需要一些手工清理。 所以尽量做到DDL与DML语句写入到不同的SQL文件中,尽量保证DDL语句的正确性,因为一旦出错需要手动清理。
Schema历史表 为了跟踪各个版本的迁移在何时由谁执行,Flyway向您的数据库Schema中添加了一个特殊的Schema历史表。 您可以将此表视为对针对Schema执行的所有更改的完整审计跟踪。 它还记录了迁移校验和以及迁移是否成功。
成功 迁移成功时,在Flyway的模式历史表中标记为成功。
失败回滚 当迁移失败且数据库支持DDL事务时,将回滚迁移,模式历史表中不会记录任何内容。
回滚失败 当迁移失败且数据库不支持DDL事务时,迁移将在模式历史表中标记为failed,这表明可能需要手动数据库清理。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。