英文:
SQLite in-memory database encounters SQLITE_LOCKED_SHAREDCACHE intermittently
问题
Below is your provided content translated into Chinese:
我正在使用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类,db.url
是正确的。
我成功地通过下面的mybatis配置设置了正确的事务隔离级别,尽管根据我报告的这个问题中有一个read_uncommitted属性的拼写错误。
<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
===============================================================
附件:
异常如下:
org.apache.ibatis.exceptions.PersistenceException:
### 查询数据库时出错。原因:org.apache.ibatis.transaction.TransactionException: 配置AutoCommit时出错。您的驱动程序可能不支持getAutoCommit()或setAutoCommit()。所请求的设置:false。原因:org.sqlite.SQLiteException: [SQLITE_LOCKED_SHAREDCACHE] 与共享缓存的其他数据库连接发生争用(数据库表被锁定)
### 错误可能存在于mapper/MsgRecordDO-sqlmap-mappering.xml
### 错误涉及com.super.mock.platform.agent.dal.daointerface.MsgRecordDAO.getRecord
### 执行查询时发生错误
### 原因:org.apache.ibatis.transaction.TransactionException: 配置AutoCommit时出错。您的驱动程序可能不支持getAutoCommit()或setCommit()。所请求的设置:false。原因:org.sqlite.SQLiteException: [SQLITE_LOCKED_SHAREDCACHE] 与共享缓存的其他数据库连接发生争用(数据库表被锁定)
英文:
I am using mybatis 3.4.6 along with org.xerial:sqlite-jdbc 3.28.0. Below is my configuration to use an in-memory database with shared mode enabled
db.driver=org.sqlite.JDBC
db.url=jdbc:sqlite:file::memory:?cache=shared
The db.url
is correct according to this test class
And I managed to setup the correct transaction isolation level with below mybatis configuration though there is a typo of property read_uncommitted according to this issue which is reported by me as well
<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>
This line of configuration
<property name="defaultTransactionIsolationLevel" value="1" />
does the trick to set the correct value of PRAGMA read_uncommitted
I am pretty sure of it since I debugged the underneath code which initialize the connection and check the value has been set correctly
However with the above setting, my program still encounters SQLITE_LOCKED_SHAREDCACHE intermittently while reading, which I think it shouldn't happen according the description highlighted in the red rectangle of below screenshot. I want to know the reason and how to resolve it, though the occurring probability of this error is low.
Any ideas would be appreciated!!
The debug configurations is below
===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
===============================================================
Attachments:
The exception is below
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)
答案1
得分: 1
我最终自己解决了这个问题,并在下面分享解决方法,以防将来有人遇到类似的问题。
通过回调指示的源代码,我们得出以下发现。
- SQLite内置了默认启用的_auto commit_,这与MyBatis相矛盾,因为我们使用SqlSessionManager,默认情况下禁用_auto commit_
- MyBatis会在连接初始化期间覆盖自动提交属性,使用方法
setDesiredAutoCommit
,最终调用SQLiteConnection#setAutoCommit
SQLiteConnection#setAutoCommit
将针对数据库执行立即开始操作,实际上是排他的,请查看下面的源代码截图以获取详细说明,因为我们将事务模式配置为IMMEDIATE
<property name="driver.transaction_mode" value="IMMEDIATE"/>
因此,到目前为止,明显的解决方案是将事务模式更改为DEFERRED。此外,还考虑了使MyBatis和SQLite之间的_auto commit_设置相同的解决方案,但是由于在初始化阶段无法设置SQLiteConnection的自动提交,所以没有采纳,如果事务模式未正确设置,则切换(从true到false或反之亦然)将可能导致上述错误。
英文:
I finally resolved this issue by myself and share the workaround below in case someone else encounters similar issue in the future.
First of all, we're able to get the completed call stack of the exception shown below
Going through the source code indicated by the callback, we have below findings.
- SQLite is built-in with auto commit enabled by default which is contradict with MyBatis which disables auto commit by default since we're using SqlSessionManager
- MyBatis would override the auto commit property during connection initialization using method
setDesiredAutoCommit
which finally invokesSQLiteConnection#setAutoCommit
SQLiteConnection#setAutoCommit
would incur a begin immediate operation against the database which is actually exclusive, check out below source code screenshots for detailed explanation since we configure our transaction mode to be IMMEDIATE
<property name="driver.transaction_mode" value="IMMEDIATE"/>
So until now, An apparent solution is to change the transaction mode to be DEFERRED. Furthermore, the solution of making the auto commit setting the same between MyBatis and SQLite has been considered as well, however, it's not adopted since there is no way to set the auto commit of SQLiteConnection during initialization stage, there would be always switching (from true to false or vice versa) and switch would cause the above error probably if transaction mode is not set properly
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论