【问题标题】:How to avoid Flyway Migrations locking the SQLite Database?如何避免 Flyway Migrations 锁定 SQLite 数据库?
【发布时间】:2015-08-03 05:07:59
【问题描述】:

我们使用 SQLite 进行 Flyway 迁移 (Flyway v3.2.1、SQLite 3.7.13 和 org.xerial:sqlite-jdbc:3.8.7 驱动程序。)

并行数据库连接的最大数量似乎至关重要。但是由于不同的原因,无论连接池大小如何,迁移都会失败。

1。使用大小为 2 或更大的连接池

存在与多个并行数据库连接相关的锁定问题。 空 SQLite 数据库上的单个迁移最终会得到:

o.f.core.internal.command.DbMigrate.(:)()  Current version of schema "main": << Empty Schema >>
o.f.core.internal.command.DbMigrate.(:)()  Migrating schema "main" to version 1 - initial
o.f.c.i.u.jdbc.TransactionTemplate.(:)()  Unable to restore autocommit to original value for connection
java.sql.SQLException: database is locked
    at org.sqlite.core.DB.throwex(DB.java:859) ~[sqlite-jdbc-3.8.7.jar:na]
    at org.sqlite.core.DB.exec(DB.java:142) ~[sqlite-jdbc-3.8.7.jar:na]
    at org.sqlite.jdbc3.JDBC3Connection.setAutoCommit(JDBC3Connection.java:152) ~[sqlite-jdbc-3.8.7.jar:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_71]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_71]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_71]
    at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_71]
    at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:126) ~[tomcat-jdbc-7.0.56.jar:na]
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:109) ~[tomcat-jdbc-7.0.56.jar:na]
    at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:80) ~[tomcat-jdbc-7.0.56.jar:na]
    at com.sun.proxy.$Proxy76.setAutoCommit(Unknown Source) ~[na:na]
    at org.flywaydb.core.internal.util.jdbc.TransactionTemplate.execute(TransactionTemplate.java:96) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.internal.command.DbMigrate.applyMigration(DbMigrate.java:282) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.internal.command.DbMigrate.access$800(DbMigrate.java:46) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.internal.command.DbMigrate$2.doInTransaction(DbMigrate.java:207) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.internal.command.DbMigrate$2.doInTransaction(DbMigrate.java:156) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.internal.util.jdbc.TransactionTemplate.execute(TransactionTemplate.java:72) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.internal.command.DbMigrate.migrate(DbMigrate.java:156) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.Flyway$1.execute(Flyway.java:1059) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.Flyway$1.execute(Flyway.java:1006) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.Flyway.execute(Flyway.java:1418) [flyway-core-3.2.1.jar:na]
    at org.flywaydb.core.Flyway.migrate(Flyway.java:1006) [flyway-core-3.2.1.jar:na]
    ...

这似乎是一个普遍的 SQLite 陷阱,如 here 所述。

通常,Flyway 迁移以及我们剩余的应用程序都会经常遇到这种类型的锁定错误:

java.sql.SQLException: [SQLITE_BUSY] 数据库文件被锁定

当两个或多个连接处于活动状态时。这就是我们尝试的原因...

2。 ...使用大小为 1 的连接池

我们将并行数据库连接的数量减少到一个(通过将我们的 Tomcat JDBC 连接池配置为“maxActive=1”)。

但是现在,Flyway 失败了

 ... org.flywaydb.core.api.FlywayException: Unable to obtain Jdbc connection from DataSource
    at org.flywaydb.core.internal.util.jdbc.JdbcUtils.openConnection(JdbcUtils.java:56)
    at org.flywaydb.core.Flyway.execute(Flyway.java:1386)
    at org.flywaydb.core.Flyway.migrate(Flyway.java:1006)
    (...)
Caused by: org.apache.tomcat.jdbc.pool.PoolExhaustedException: [localhost-startStop-1] Timeout: Pool empty. Unable to fetch a connection in 10 seconds, none available[size:1; busy:1; idle:0; lastwait:10000].
    at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:674)
    at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:188)
    at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:128)
    at org.flywaydb.core.internal.util.jdbc.JdbcUtils.openConnection(JdbcUtils.java:50)

为什么? Flyway 迁移似乎需要两个并行连接。 Here 他们有一个相关的问题并表示: “看来 Flyway 至少使用了两种连接:一种用于锁定 schema_version 表,另一种用于实际运行更改……”


最后,我想知道 Flyway 是如何支持 SQLite 的。两个连接是强制性的,但数据库最终被锁定。

但是,SQLite 是 officially supported,我认为我搞砸了。

Flyway 如何与 SQLite 完美配合?

【问题讨论】:

  • 根据:github.com/flyway/flyway/blob/master/flyway-core/src/main/java/…“无法锁定”+this+“因为SQLite不支持锁定。不支持并发迁移。”);
  • 对于“并发迁移”,它们意味着 2+ 个应用程序在同一个数据库上运行它们的迁移(另请参阅 stackoverflow.com/questions/27914833/…),但这不是我们的用例。我设法通过一次非并发迁移失败了。
  • 我还注意到flyway中的sqlite集成测试使用了memfile,所以它可能根本无法使用实际文件。
  • 有了一个内存数据库——我为了好奇而测试过——迁移仍然挂起。但是这次使用表锁而不是整个数据库锁。该测试配置了“共享缓存”,它允许 SQLite 连接共享相同的内存数据库。 (不过,与此同时,Axel Fontaine 对 SingleConnectionDataSource 解决方案的看法是正确的。)

标签: java sqlite flyway


【解决方案1】:

使用像 Spring 的 SingleConnectionDataSource 这样的东西,你应该会很好。

更新:Flyway 4.1 现在在迁移时自动只使用一个连接,这不再需要了。

【讨论】:

  • 我有同样的问题,但我不使用弹簧。还有什么可以在 SQLite 数据库上运行迁移?
  • 根据this release note,现在Flyway 4.1.0及更高版本内置了SQLite的这种单连接方式:SQLite支持现在也基于新的单连接方式,避免各种潜在的锁定问题
【解决方案2】:

不幸的是,Flyway 使用两个到数据库的连接来完成它的工作(一个用于元数据表,一个用于用户对象)。我不确定这是否是一个要求,但无论如何,因为它需要两个连接,它不能被大小为 1 的标准连接池使用。此外,即使你的池中有 2 个连接,你也有可能如果多个线程启动并使用 Flyway,则会出现死锁。每个线程可能会获得它需要的第一个连接并无限期地阻塞等待第二个。实际上,我只是在我们的产品中遇到了这个问题,我们经常与 Flyway 交谈以检查各种租户的架构版本。此问题适用于池的任何使用,并非特定于 SQLite。

【讨论】:

  • 发布此内容后,我深入研究了他们的源代码,发现他们在 4.1 版中解决了这个问题。因此,如果您遇到此类问题,请确保您使用的是 4.1+
  • 更新: 从 Flyway 4.1.0 及更高版本根据this release noteSQLite 支持现在也基于新的单连接模式,避免各种潜在的锁定问题
猜你喜欢
  • 2015-01-07
  • 2011-08-31
  • 2011-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 2023-04-08
  • 2014-05-08
相关资源
最近更新 更多