3

我有一个使用 Seam & Hibernate(JDBC 到 SQLServer)的 Seam Web 应用程序。

它运行良好,但在重负载下(使用 JMeter 进行压力测试),我有一些LockAcquisitionExceptionor OptimisticLockException.

LockAquisitionException是由SQLServerException “事务(进程 ID 64)与另一个进程在锁定资源上发生死锁并已被选为死锁受害者。重新运行事务引起的。

然后,我编写了一个接缝拦截器来重新运行此类事务LockAquisitionException

@AroundInvoke
public Object aroundInvoke(final InvocationContext invocationContext) throws Exception {
    if (instanceThreadLocal.get() == null && isMethodInterceptable(invocationContext)) {
        try {
            instanceThreadLocal.set(this);

            int i = 0;
            PersistenceException exception = null;
            do {
                try {
                    return invocationContext.proceed();
                } catch (final PersistenceException e) {
                    final Throwable cause = e.getCause();
                    if (!(cause instanceof LockAcquisitionException)) {
                        throw e;
                    }
                    exception = e;
                    i++;
                    if (i < MAX_RETRIES_LOCK_ACQUISITION) {
                        log.info("Swallowing a LockAcquisitionException - #0/#1", i, MAX_RETRIES_LOCK_ACQUISITION);
                        try {
                            if (Transaction.instance().isRolledBackOrMarkedRollback()) {
                                Transaction.instance().rollback();
                            }
                            Transaction.instance().begin();
                        } catch (final Exception e2) {
                            throw new IllegalStateException("Exception while rollback the current transaction, and begining a new one.", e2);
                        }
                        Thread.sleep(1000);
                    } else {
                        log.info("Can't swallow any more LockAcquisitionException (#0/#1), will throw it.", i, MAX_RETRIES_LOCK_ACQUISITION);
                        throw e;
                    }
                }
            } while (i < MAX_RETRIES_LOCK_ACQUISITION);

            throw exception;

        } finally {
            instanceThreadLocal.remove();
        }
    }
    return invocationContext.proceed();
}

第一个问题:你认为这个拦截器能正确完成这项工作吗?

通过谷歌搜索并看到Alfresco这里有一个论坛讨论),BonitaOrchestra也有一些方法可以重新运行此类事务,并且他们正在捕获更多异常,StaleObjectStateException例如(我的原因OptimisticLockException)。

我的第二个问题如下:对于StaleObjectStateException“行已被另一个事务更新或删除(或未保存的值映射不正确)”),通常你不能只重新运行事务,因为这是与数据库和@Version字段同步的问题不是吗?例如,为什么 Alfresco 会尝试重新运行由此类异常引起的此类事务?

编辑:由于LockAcquisitionException原因SQLServerException,我查看了网络上的一些资源,即使我应该仔细检查我的代码,它似乎无论如何都会发生......这里是链接:

甚至微软也说“虽然死锁可以最小化,但不能完全避免。这就是为什么前端应用程序应该设计成处理死锁的原因。”

4

1 回答 1

2

实际上,我终于找到了如何躲避著名的“事务(进程 ID 64)与另一个进程在锁定资源上发生死锁,并已被选为死锁受害者。重新运行事务”

所以我不会真正回答我的问题,但我会解释我所看到的以及我是如何做到的。

起初,我认为我有一个“锁升级问题”,这会将我的行锁转换为页锁并产生我的死锁(我的 JMeter 测试运行在选择行时删除/更新的场景,但删除和更新不会'不一定涉及与选择相同的行)。

因此,我阅读了 SQL2005 中的锁升级如何解决由 SQL Server 中的锁升级引起的阻塞问题(由 MS),最后使用 sp_lock 诊断 SQL Server 性能问题

但是在尝试检测我是否处于锁定升级情况之前,我落在了那个页面上:http: //community.jboss.org/message/95300。它谈到了“事务隔离”,并且 SQLServer 有一个特殊的称为“快照隔离”。

然后,我发现Using Snapshot Isolation with SQL Server and Hibernate并阅读Using Snapshot Isolation (by MS)。

所以我首先在我的数据库上启用了“快照隔离模式”:

ALTER DATABASE [MY_DATABASE]
SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE [MY_DATABASE]
SET READ_COMMITTED_SNAPSHOT ON

然后我必须为 JDBC 驱动程序定义事务隔离4096......并通过阅读“5.1.6 设置隔离级别”段落中的“Hibernate in Action”一书,内容如下:

请注意,Hibernate 永远不会更改从托管环境中的应用程序服务器提供的数据源获得的连接的隔离级别。您可以使用应用程序服务器的配置更改默认隔离

因此,我阅读了配置 JDBC 数据源(针对 JBoss 4),最后编辑了我的database-ds.xml文件以添加以下内容:

<local-tx-datasource>
    <jndi-name>myDatasource</jndi-name>
    <connection-url>jdbc:sqlserver://BDDSERVER\SQL2008;databaseName=DATABASE</connection-url>
    <driver-class>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver-class>
    <user-name>user</user-name>
    <password>password</password>
    <min-pool-size>2</min-pool-size>
    <max-pool-size>400</max-pool-size>
    <blocking-timeout-millis>60000</blocking-timeout-millis>
    <background-validation>true</background-validation>
    <background-validation-minutes>2</background-validation-minutes>
    <idle-timeout-minutes>15</idle-timeout-minutes>
    <check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
    <prefill>true</prefill>
    <prepared-statement-cache-size>75</prepared-statement-cache-size>
    <transaction-isolation>4096</transaction-isolation>
</local-tx-datasource>

最重要的部分当然是<transaction-isolation>4096</transaction-isolation>

然后,我再也没有死锁问题了!......所以我的问题现在对我来说或多或少没用......但也许有人可以有一个真正的答案!

于 2011-07-20T14:23:28.303 回答