有时我会遇到锁定问题,例如:
java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested
我正在使用带有 C3p0 池的休眠,并将休眠配置为乐观锁定。
我还有一些绕过Hibernate并通过独立配置的c3p0池与数据库通信的代码。这纯粹是因为这段代码在我搬到Hibernate之前就已经存在了,并且运行良好,所以我认为当时没有必要更改它。
现在我想知道有两个独立配置的 c3p0 池是否会导致问题。如果不是,我如何追踪这些异常的原因,我将池化设置为 20 到 100 个连接,并且我最多只有 12 个线程并发,我认为我的所有事务/会话都在我完成它们时被关闭。
编辑:现在有一个池,但仍然遇到问题,得到以下错误但没有关于它的原因的详细信息,我注意到的一件事是它总是说托管线程:3
Exception with lookup
12:42:36,627 WARN ThreadPoolAsynchronousRunner:608 - com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector@1ff96a2 -- APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks!
12:42:36,628 WARN ThreadPoolAsynchronousRunner:624 - com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector@1ff96a2 -- APPARENT DEADLOCK!!! Complete Status:
Managed Threads: 3
Active Threads: 3
Active Tasks:
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StatementCloseTask@fdfb9a (com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0)
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StatementCloseTask@914847 (com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#2)
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StatementCloseTask@205390 (com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#1)
Pending Tasks:
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StatementCloseTask@4e171b
com.mchange.v2.resourcepool.BasicResourcePool$1RefurbishCheckinResourceTask@ceeecb
com.mchange.v2.resourcepool.BasicResourcePool$1RefurbishCheckinResourceTask@19f7cec
com.mchange.v2.resourcepool.BasicResourcePool$1RefurbishCheckinResourceTask@1c299f9
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StmtAcquireTask@10ab38a
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StmtAcquireTask@1916a2f
com.mchange.v2.resourcepool.BasicResourcePool$1RefurbishCheckinResourceTask@1d23fbf
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StmtAcquireTask@573b7c
com.mchange.v2.resourcepool.BasicResourcePool$1RefurbishCheckinResourceTask@1027733
com.mchange.v2.resourcepool.BasicResourcePool$1RefurbishCheckinResourceTask@dfd9b0
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StmtAcquireTask@4cecbb
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StatementCloseTask@4a0d0b
com.mchange.v2.resourcepool.BasicResourcePool$1RefurbishCheckinResourceTask@19e809d
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StmtAcquireTask@10de0f8
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StmtAcquireTask@2ce568
Pool thread stack traces:
Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0,5,JAIKOZ Thread Group]
org.apache.derby.impl.jdbc.EmbedStatement.close(Unknown Source)
com.mchange.v1.db.sql.StatementUtils.attemptClose(StatementUtils.java:41)
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StatementCloseTask.run(GooGooStatementCache.java:404)
com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)
Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#2,5,JAIKOZ Thread Group]
org.apache.derby.impl.jdbc.EmbedStatement.close(Unknown Source)
com.mchange.v1.db.sql.StatementUtils.attemptClose(StatementUtils.java:41)
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StatementCloseTask.run(GooGooStatementCache.java:404)
com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)
Thread[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#1,5,JAIKOZ Thread Group]
org.apache.derby.impl.jdbc.EmbedStatement.close(Unknown Source)
com.mchange.v1.db.sql.StatementUtils.attemptClose(StatementUtils.java:41)
com.mchange.v2.c3p0.stmt.GooGooStatementCache$1StatementCloseTask.run(GooGooStatementCache.java:404)
com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)
可能是这个问题
https://forum.hibernate.org/viewtopic.php?p=2390809
鉴于SQLTransactionRollbackException
是数据库级锁定故障,仅独立配置的 c3p0 池不是导致此问题的原因。 如果是这种情况,您将无法运行同一个基于 Hibernate 的应用程序的两个实例。
此处的第一步应该是使用调试器在引发异常时停止。 然后检查其他数据库连接池线程,查看它们是否正在对数据库执行任何操作。 如果是,这将是第一个要查找的地方,因为您可能会遇到由数据库级锁引起的死锁。 如果可以减少池中的线程数,同时仍重现问题,则此步骤可能会更容易。
但原因可能是数据库锁被另一个线程获取,然后未释放。 如果是这种情况,您必须使用数据库工具来找出异常抛出线程无法获取的资源,然后尝试找出谁拥有该锁以及原因。
祝你好运。
我在嵌入式环境中遇到了Hibernate的问题。我们使用SQLite来执行客户端数据库操作。我发现嵌入式数据库处理多线程操作的方法有限或粗糙。在提交甚至可能开始任何事务之前,您可能必须检查数据库上的锁定。
使用 SQLite,您可以让线程在事务正在进行时进入读取。但是,不能让一个或多个线程同时写入或打开事务。此外,很难找到真正的粘性,您无法在任何查询发生时提交交易!否则,您将遇到类似于您遇到的锁定异常。
我实际上创建了一个信号量闩锁来跟踪所有打开的读取操作并实现与事务的安全交互:
/**
* Enter read section. Increment the latch so commiting
* threads know how many reads are left till it's appropriate
* to write/commit.
*
* @throws InterruptedException the interrupted exception
*/
public void enterReadSection() throws InterruptedException {
if (enableReadLock && (transactionLock.availablePermits()==0)) {
readLock.lock();
try {
log.debug("Waiting on database unlock.");
readWait.await();
} finally {
readLock.unlock();
log.debug("Database Unlocked.");
}
}
if (enableReadLock) {
synchronized(this) {
latch = new CountDownLatch((int)latch.getCount()+1);
}
}
}
/**
* Exit read section.
*/
public void exitReadSection( ) {
if (enableReadLock)
latch.countDown();
}
/**
* Trx lock.
*
* @throws InterruptedException the interrupted exception
*/
public void trxLock() throws InterruptedException {
if (enableTrxLock)
transactionLock.acquire();
}
/**
* Trx unlock.
*/
public void trxUnlock() {
if (enableTrxLock)
transactionLock.release();
}
/**
* Commit lock.
*
* @throws InterruptedException the interrupted exception
*/
public void commitLock() throws InterruptedException {
if (enableCommitLock) {
commitLock.acquire();
//Wait for reading threads to complete
latch.await();
}
}
/**
* Commit unlock.
*
* @throws InterruptedException the interrupted exception
*/
public void commitUnlock() throws InterruptedException {
if(enableCommitLock) {
commitLock.release();
releaseRead();
}
}
我知道,那里正在发生一些深奥的魔法,但这是我在经过许多跟踪和错误之后想出的。我希望它可以更简单,但通常数据库的客户端/服务器前端会卸载大部分并发问题,所以你不会在 Oracle、mysql、postgres 等上看到它......
我想在使用 derby 时,您也需要某种并发检查机制、信号量或锁存器。我对 derby 了解不多,但听起来它也内置了原始的线程安全机制。您可能必须解决这些限制。祝你好运!