【问题标题】:Problems with Transaction Locking with Hibernate 4.0Hibernate 4.0 的事务锁定问题
【发布时间】:2012-04-28 00:13:19
【问题描述】:

有时我会遇到锁定问题,例如:

java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested

我正在使用带有 C3p0 池的 Hibernate,并将 Hibernate 配置为乐观锁定。

我也有一些代码绕过休眠并通过独立配置的 c3p0 池与数据库对话。这纯粹是因为这段代码在我迁移到 Hibernate 之前就已经存在并且运行良好,所以我当时认为没有必要更改它。

现在我想知道拥有两个独立配置的 c3p0 池是否会导致问题。如果不是,我如何追查这些异常的原因,我将池设置为 20 到 100 个连接,并且我最多同时有 12 个线程,我认为当我完成它们时我的所有事务/会话都将被关闭。

编辑:现在有单个池但仍然出现问题,出现以下错误但没有详细说明其原因,我注意到的一件事是它总是说 Managed Thread: 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

【问题讨论】:

  • 如果更好的话,我可能会搬到 h2
  • H2 确实支持多版本并发。它应该有助于避免使用互斥锁和锁,如下面的示例。尝试一下可能不会有什么坏处。

标签: java hibernate locking timeout derby


【解决方案1】:

鉴于SQLTransactionRollbackException 是数据库级别的锁定故障,单独配置的c3p0 池不是导致此问题的原因。如果是这种情况,您将无法运行同一个基于 Hibernate 的应用程序的两个实例。

这里的第一步应该是在抛出异常时使用调试器停止。然后检查其他数据库连接池线程,看看它们是否正在对数据库进行任何操作。如果是,那将是第一个查看的地方,因为您可能遇到了由数据库级锁引起的死锁。如果您可以在仍然重现问题的同时减少池中的线程数,则此步骤可能会更容易。

也有可能是另一个线程获得了数据库锁,然后没有释放。如果是这种情况,您将不得不使用您的数据库工具找出抛出异常的线程无法获取哪些资源,然后尝试找出谁拥有该锁以及原因。

祝你好运。

【讨论】:

  • 好吧,我确实更改了代码,问题似乎已经消失了,但我确实必须更改其他一些东西才能做到这一点,所以也许你是对的,这是一个巧合。我感到困惑的一件事是让 Hibernate 会话打开的时间更长会导致它持有数据库锁的时间更长,以前我认为不会,但我现在不太确定。
  • 如果你做的事情会得到锁,并且从不告诉会话你已经完成了,那么是的,这些锁仍然会被持有。换句话说,我当然希望会话打开的任何数据库锁都在 .close() 中关闭,如果在事务中,则在 .commit() 中关闭。如果你从不调用这些方法,它们会被保留一段时间,具体取决于数据库设置。
  • 我总是关闭并提交,但如果会话打开了一段时间并没有真正帮助,我不知道如何找出 Hibernate 何时真正获得锁
  • 这确实是一个数据库问题,而不是休眠问题。这取决于正在运行的 SQL、事务设置以及 te db 实现的能力:例如,行级锁定或表?您可以通过单步执行代码并使用数据库特定工具查看哪些锁未完成来跟踪它。
【解决方案2】:

我在嵌入式环境中遇到过 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 了解不多,但听起来它也内置了原始的线程安全机制。您可能必须解决这些限制。祝你好运!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 2011-01-07
    相关资源
    最近更新 更多