SQLite内存数据库间歇性遇到SQLITE_LOCKED_SHAREDCACHE

问题描述

我正在使用mybatis 3.4.6和org.xerial:sqlite-jdbc 3.28.0。以下是我的配置,以使用启用了共享模式的内存数据库

db.driver=org.sqlite.JDBC
db.url=jdbc:sqlite:file::memory:?cache=shared

根据test classdb.url是正确的

尽管我也报告了根据此issue属性read_uncommitted的错字,但我还是设法在mybatis配置以下设置了正确的事务隔离级别

<environment id="${db.env}">
    <transactionManager type="jdbc"/>
    <dataSource type="POOLED">
        <property name="driver" value="${db.driver}" />
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}" />
        <property name="password" value="${db.password}" />
        <property name="defaultTransactionIsolationLevel" value="1" />
        <property name="driver.synchronous" value="OFF" />
        <property name="driver.transaction_mode" value="IMMEDIATE"/>
        <property name="driver.foreign_keys" value="ON"/>
    </dataSource>
</environment>

这行配置

  <property name="defaultTransactionIsolationLevel" value="1" />

可以设置正确的 PRAGMA read_uncommitted

我很确定,因为我调试了用于初始化连接并检查值是否已正确设置的代码

但是,使用上述设置,我的程序在阅读时仍会间歇性地遇到sqlITE_LOCKED_SHAREDCACHE,根据以下屏幕截图中红色矩形突出显示的说明,我认为它不应该发生。我想知道原因和解决方法,尽管发生此错误的可能性很低。

任何想法将不胜感激!

调试配置如下


===CONFINGURATION==============================================
 jdbcDriver                     org.sqlite.JDBC
 jdbcUrl                        jdbc:sqlite:file::memory:?cache=shared
 jdbcUsername                   
 jdbcPassword                   ************
 poolMaxActiveConnections       10
 poolMaxIdleConnections         5
 poolMaxCheckoutTime            20000
 poolTimetoWait                 20000
 poolPingEnabled                false
 poolPingQuery                  NO PING QUERY SET
 poolPingConnectionsNotUsedFor  0
 ---STATUS-----------------------------------------------------
 activeConnections              5
 idleConnections                5
 requestCount                   27
 averageRequestTime             7941
 averageCheckoutTime            4437
 claimedOverdue                 0
 averageOverdueCheckoutTime     0
 hadToWait                      0
 averageWaitTime                0
 badConnectionCount             0
===============================================================

enter image description here

附件:

下面是例外

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.transaction.TransactionException: Error configuring AutoCommit.  Your driver may not support getAutoCommit() or setAutoCommit(). Requested setting: false.  Cause: org.sqlite.sqliteException: [sqlITE_LOCKED_SHAREDCACHE]  Contention with a different database connection that shares the cache (database table is locked)
### The error may exist in mapper/MsgRecordDO-sqlmap-mappering.xml
### The error may involve com.super.mock.platform.agent.dal.daointerface.MsgRecordDAO.getRecord
### The error occurred while executing a query
### Cause: org.apache.ibatis.transaction.TransactionException: Error configuring AutoCommit.  Your driver may not support getAutoCommit() or setAutoCommit(). Requested setting: false.  Cause: org.sqlite.sqliteException: [sqlITE_LOCKED_SHAREDCACHE]  Contention with a different database connection that shares the cache (database table is locked)

解决方法

我终于自己解决了这个问题,并在下面分享了变通办法,以防将来其他人遇到类似问题。

首先,我们可以获取如下所示的异常的完整调用堆栈 enter image description here

遍历回调指示的源代码,我们有以下发现。

  1. SQLite内置默认启用了 auto commit ,这与MyBatis矛盾,因为MyBatis默认禁用 auto commit ,因为我们使用的是SqlSessionManager
  2. MyBatis将在使用方法setDesiredAutoCommit的连接初始化期间重写自动提交属性,该方法最终将调用SQLiteConnection#setAutoCommit
  3. SQLiteConnection#setAutoCommit将对数据库产生立即开始操作,该操作实际上是排他的,请查看下面的源代码屏幕截图以获取详细说明,因为我们将交易模式配置为 IMMEDIATE

<property name="driver.transaction_mode" value="IMMEDIATE"/>

enter image description here enter image description here

因此,到目前为止,一个显而易见的解决方案是将交易模式更改为 DEFERRED 。此外,还考虑了使MyBatis和SQLite之间的 auto commit 设置相同的解决方案,但是,由于在初始化阶段无法设置SQLiteConnection的自动提交,因此未采用该解决方案,总是会发生 switching (从true到false,反之亦然),并且 switch 可能会导致上述错误,如果交易模式设置不正确