【发布时间】:2017-01-03 04:42:15
【问题描述】:
多年来,我们一直使用flywayDB 来管理在 oracle 12c 上运行的相当大的数据库应用程序。这工作正常且非常可靠。
但是最近我们在数据库迁移过程中遇到了性能问题。我们在版本表中管理的数据库脚本数量已超过 10,000 个。迁移单个脚本所需的时间已从最初的几毫秒增加到目前的大约一秒。似乎每个迁移步骤的 flyway 都会选择版本表的全部内容来在客户端计算其安装排名。这不能很好地扩展。
是否有可能加快飞行速度,可能是通过缓存版本表的内容?
我们正在使用flyway 3.2.1版的Java-API。
为了完整起见,我编写了一个测试用例来演示这种行为。
@RunWith(Parameterized.class)
public class PerformanceTestcase {
private static Logger LOG = Logger.getLogger( PerformanceTestcase.class.getName() );
@Parameter
public int noOfScripts;
@Before
public void generateLotsOfInstallerSkripts() throws IOException {
LOG.log(Level.INFO, "generating {0} skripts", noOfScripts);
Path baseVersion = getBaseVersionPath();
generateSkripts( noOfScripts, baseVersion, BASE_SKRIPT_NAME );
}
@Test
public void testPerformance() throws IOException, SQLException {
// this one does not scale well with increasing noOfScripts
migrate();
}
private static final String SCHEMA_TABLE_NAME = "test_versions";
private static final String SKRIPT_NAME_FORMAT = "%s.%05d__test.sql";
private static final String SKRIPT_CONTENT = "select %05d from dual;";
private static final String FILESYSTEM = "filesystem:";
private static final String BASE_SKRIPT_NAME = "V00.00.00";
private static final String BASE_DIR = "/tmp/performanceTest";
private void migrate() throws SQLException {
Flyway flyway = new Flyway();
flyway.setDataSource( getDataSource() );
flyway.setLocations( FILESYSTEM + BASE_DIR );
flyway.setTable( SCHEMA_TABLE_NAME );
flyway.setBaselineVersionAsString(BASE_SKRIPT_NAME.substring(1) );
flyway.setBaselineOnMigrate(true);
flyway.setValidateOnMigrate(false);
flyway.migrate();
}
@Parameters(name="noOfScripts={0}")
public static Iterable<? extends Object> data() {
List<Integer> retval = new LinkedList<Integer>();
for ( int i=0; i<16000; i+=1000 ) {
if ( i>0 ) retval.add( Integer.valueOf(i) );
retval.add( Integer.valueOf(i+100) );
}
return retval;
}
private Path getBaseDirPath() throws IOException {
Path base = Paths.get(BASE_DIR);
if ( !Files.exists(base) ) {
Files.createDirectory(base);
}
return base;
}
private Path getBaseVersionPath() throws IOException {
Path base = getBaseDirPath();
Path baseVersion = base.resolve(BASE_SKRIPT_NAME);
if ( !Files.exists(baseVersion) ) {
Files.createDirectories(baseVersion);
}
return baseVersion;
}
private void generateSkripts( int numberOfSkripts, Path baseDir, String baseName ) throws IOException {
for (int i = 0; i < numberOfSkripts; i++) {
Path file = baseDir.resolve( String.format(SKRIPT_NAME_FORMAT, baseName, i) );
Files.write( file
, Arrays.asList( new String[] { String.format( SKRIPT_CONTENT, i ) } )
, StandardOpenOption.CREATE
, StandardOpenOption.TRUNCATE_EXISTING
);
}
}
private DataSource getDataSource() throws SQLException {
OracleDataSource ds = new OracleDataSource();
ds.setURL(CONNECTION_URL);
return ds;
}
}
更新
我只是使用 flywayDB 的当前版本 4.0.3 运行测试用例。与 3.2.1 相比,它运行了大约一半的时间,但缩放问题仍然存在。 Flyway 会在每个迁移步骤中选择完整的版本表,当版本表非常填充时,这会显着减慢迁移速度。
再次更新
我查看了 flywayDB 4.0.3 版的源代码:在 org.flywaydb.core.internal.command.DbMigrate#migrate 中创建并刷新了 MigrationInfoServiceImpl。这将选择完整的 schema_versions-Table。但在那一步之后,只执行一个迁移脚本。我希望迁移所有待处理的脚本。
我在 github 上打开了一个issue。
【问题讨论】:
-
SQL 脚本是存储在数据库中的表中还是作为文件系统中的文件?
-
SQL 脚本是来自文件系统的文件。
-
您是否分析了流程中每个步骤的性能?我怀疑 Oracle 可以在眨眼之间读取一个包含 10000 条记录的表,我不太确定 Java 应用程序和操作系统必须索引和读取磁盘上的 10000 个文件。
-
是的,当然。分析器通过计算安装等级给出了该方法的提示。这似乎是客户端的一个问题。数据库负载非常低。
-
好的。我不认为我可以帮助你,抱歉 :-) 我理解你的问题,因为你正在查看数据库性能问题。