Flyway维护多数据源下的表
背景:新项目基于一个老项目进行,老项目是一个分库的项目(线上多个机器运行MySQL实例,每个实例有N个分库,数据按照一定规则分库存储)。新项目也是分库存储。
要求:方便地维护数据库。不同数据源的多个分库下有相同结构的表,更新其中一个表结构,其他数据源的不同分库下的该类表结构也会更新(业务限定了分库下的表结构一致)。
工具:Flyway
1.引入依赖
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>5.2.4</version>
</dependency>
2.书写配置
spring:
profiles:
active: '@spring.profiles@'
flyway:
enabled: true
baseline-on-migrate: true
validate-on-migrate: false
placeholderReplacement: false
ignoreMissingMigrations: true
locations: "classpath:db/dlcms"#由于线上是多源数据,代码启动时在FlywayConfig配置类中转换数据源
其中比较关键的配置是locations
,该路径下存放要执行的sql脚本。
3.SQL脚本存放
Flyway配置的SQL脚本存放位置是"classpath:db/dlcms"
,但实际上还可以根据业务需求存放到其他位置,便于管理。但要读取到其他地方的SQL文件,就需要自定义执行migrate()
。
4.执行migrate()
一般单数据源,不必自己去执行该方法,启动就会检查版本并执行locations配置的路径下的脚本。但在多数据源且存在分库的情况下,为了简化sql脚本需要手动执行migrate()
,便于管理。
利用SpringBoot的配置类,在项目启动的时候就运行该方法,等数据库更新完后,项目才能算启动成功。
配置类代码:
@Slf4j
@Configuration
public class FlywayConfig {
@Resource
private InstanceMapCache instanceMapCache;
@Value("${spring.flyway.locations}")
private String dlcms;
@Value("${spring.flyway.baseline-on-migrate}")
private boolean baselineOnMigrate;
@Value("${spring.flyway.validate-on-migrate}")
private boolean validateOnMigrate;
@Value("${spring.flyway.placeholderReplacement}")
private boolean placeholderReplacement;
@Value("${spring.flyway.ignoreMissingMigrations}")
private boolean ignoreMissingMigrations;
@Value("${flyway.instance1}")
private String instanceList1;
@Value("${flyway.instance2}")
private String instanceList2;
@Value("${flyway.instance3}")
private String instanceList3;
@Value("${flyway.instance4}")
private String instanceList4;
private static final StringYUELI_LOCATION= "classpath:db/yueli";
private static final StringPLG_LOCATION= "classpath:db/plg";
@Resource
private PlgInitMapper plgInitMapper;
@Bean
public void migrate() {
// 启动默认连接主库
DbRouteUtil.switchToDlcms();
Map<MysqlInstanceEnum, DataSource> cache = instanceMapCache.getCache();
cache.forEach((k, v) -> {
switch (k) {
caseINSTANCE_1-> {
runDlcms(v);
DbRouteUtil.switchToInstance1();
runDlcms_XX(k, v, instanceList1);
runPlg_XX(k, v);
}
caseINSTANCE_2-> {
DbRouteUtil.switchToInstance2();
runDlcms_XX(k, v, instanceList2);
runPlg_XX(k, v);
}
caseINSTANCE_3-> {
DbRouteUtil.switchToInstance3();
runDlcms_XX(k, v, instanceList3);
runPlg_XX(k, v);
}
caseINSTANCE_4-> {
DbRouteUtil.switchToInstance4();
runDlcms_XX(k, v, instanceList4);
runPlg_XX(k, v);
}
}
});
}
public String[] getDlcmsList(String s) {
if (StringUtils.hasLength(s)) {
return s.split(",");
} else {
return new String[0];
}
}
/**
*主库执行
*/
private void runDlcms(DataSource v){
Flyway flyway = Flyway.configure()
.dataSource(v)
.locations(dlcms)
.schemas("dlcms")
.baselineOnMigrate(baselineOnMigrate)
.validateOnMigrate(validateOnMigrate)
.placeholderReplacement(placeholderReplacement)
.ignoreMissingMigrations(ignoreMissingMigrations)
.load();
flyway.migrate();
}
private void runDlcms_XX(MysqlInstanceEnum k,DataSource v,String instance){
String[] dlcmsXXList = getDlcmsList(instance);
if (dlcmsXXList.length == 0) {
return;
}
for (String dlcms_xx : dlcmsXXList) {
Flyway flyway = Flyway.configure()
.dataSource(v)
.locations(YUELI_LOCATION)
.schemas(dlcms_xx)
.baselineOnMigrate(baselineOnMigrate)
.validateOnMigrate(validateOnMigrate)
.placeholderReplacement(placeholderReplacement)
.ignoreMissingMigrations(ignoreMissingMigrations)
.load();
flyway.migrate();
}
}
private void runPlg_XX(MysqlInstanceEnum k,DataSource v){
List<String> plgSchemaAll = plgInitMapper.getPlgSchemaAll();
if (CollectionUtils.isEmpty(plgSchemaAll)) {
return;
}
for (String plg_xx : plgSchemaAll) {
Flyway flyway = Flyway.configure()
.dataSource(v)
.locations(PLG_LOCATION)
.schemas(plg_xx)
.baselineOnMigrate(baselineOnMigrate)
.validateOnMigrate(validateOnMigrate)
.placeholderReplacement(placeholderReplacement)
.ignoreMissingMigrations(ignoreMissingMigrations)
.load();
flyway.migrate();
}
}
}
上述代码的作用,启动时执行migrate()
,在configure
中指定配置信息,比如:
- dataSource:指定数据源
- locations:SQL脚本位置
- schemas:指定分库名,方便书写SQL不用单独指定库名(否则100个分库就要写100个语句,而仅仅是库名不同)
不同数据源的切换由AbstractRoutingDataSource
完成。
5.flyway_schema_history
交给Flyway管理的库,会检查是否有该表,否则就新建。新建该表后会生成一条记录<< Flyway Baseline >>
,版本号是1.
Flyway不会执行同样版本的SQL文件,所以自己写的SQL文件版本要避开1,如果SQL文件名为:V1.0.0__Init.sql
,则该文件并不会执行。我这里改成了2开头。
7.效果
不同数据源、不同分库,只要表结构一样,就可以按需将同类SQL存放到某个特定位置。
SQL不必写分库名,在项目启动的时候,Flyway到各个数据源的分库下去执行SQL脚本。
6.总结
- 利用SpringBoot配置类,启动时运行
migrate()
- 在
Flyway.configure()
中自定义指定配置信息,主要包括:数据源、脚本位置、分库名等
参考资料