6

我们的应用程序(使用 NHibernate 和 ASP.NET MVC)在进行压力测试时会引发很多 NHibernate 事务错误。主要类型有:

  1. 事务未连接,或已断开连接
  2. 行被另一个事务更新或删除(或未保存值映射不正确)
  3. 事务(进程 ID 177)与另一个进程在锁资源上死锁,并已被选为死锁牺牲品。重新运行事务。

有人可以帮助我确定异常 1 的原因吗?我知道我必须处理代码中的其他异常。有人可以向我指出可以帮助我以有效方式处理这些错误的资源吗?

问:我们如何管理会话和事务?

A. 我们正在使用 Autofac。对于每个服务器请求,我们创建一个新的请求容器,该容器在容器生命周期范围内具有会话。在激活会话时,我们开始事务。当请求完成时,我们提交事务。在某些情况下,交易可能是巨大的。为简化起见,每个服务器请求都包含在事务中。

4

3 回答 3

1

这个线程有一个解释: http ://groups.google.com/group/nhusers/browse_thread/thread/7f5fb68a00829d13

简而言之,数据库可能由于某些错误而自行回滚事务,因此当您稍后尝试回滚事务时,它已经回滚并处于僵尸状态。这往往会隐藏回滚的实际原因,因为您所看到的只是 TransactionException 而不是首先实际触发回滚的异常。

我认为除了记录它并试图找出导致潜在错误的原因之外,您无能为力。

于 2011-03-01T15:11:59.243 回答
1

看看这个线程: http ://n2cms.codeplex.com/Thread/View.aspx?ThreadId=85016

基本上它所说的可能导致此异常的原因是:

2010-02-17 21:01:41,204 1 WARN NHibernate.Util.ADOExceptionReporter - System.Data.SqlClient.SqlException:数据库“数据库名称”的事务日志已满。要找出日志中的空间不能被重用的原因,请参阅 sys.databases 中的 log_reuse_wait_desc 列

由于事务日志的大小与事务期间完成的工作量成正比,也许您应该考虑将事务边界放在命令处理程序“处理”事务写入部分的命令之间。然后,您将使用会话#X,加载您希望变异的状态,对其进行变异并提交,所有这些都作为#X 中的一个工作单元。

关于事物的读取端,您可能会有另一个读取数据的 ISession#Y;此 ISession 可用于批量读取,例如 RepeatableRead 或与 Futures 功能类似的东西,并且可以简单地从缓存中读取(尽管它确实是一个拐杖)。这样做可能会帮助您从不存在的“错误”中恢复;活锁、死锁和受害者事务。

每个请求使用事务的问题在于,您的 ISession 在您工作时会获取大量簿记数据,所有这些都是事务的一部分。因此,数据库将数据(rols、cols、表等)标记为参与事务,导致等待图跨越“实体”(在数据库意义上,而不是 DDD 意义上),这实际上不是一部分您的应用程序执行的命令的事务边界。

作为记录(其他人在谷歌上搜索),Fabio 有一篇处理数据层异常的帖子。引用他的一些代码;

public class MsSqlExceptionConverterExample : ISQLExceptionConverter
{
  public Exception Convert(AdoExceptionContextInfo exInfo)
  {
      var sqle = ADOExceptionHelper.ExtractDbException(exInfo.SqlException) as SqlException;
      if(sqle != null)
      {
          switch (sqle.Number)
          {
              case 547:
                  return new ConstraintViolationException(exInfo.Message,
                      sqle.InnerException, exInfo.Sql, null);
              case 208:
                  return new SQLGrammarException(exInfo.Message,
                      sqle.InnerException, exInfo.Sql);
              case 3960:
                  return new StaleObjectStateException(exInfo.EntityName, exInfo.EntityId);
          }
      }
      return SQLStateConverter.HandledNonSpecificException(exInfo.SqlException,
          exInfo.Message, exInfo.Sql);
  }
}
  • 547 是约束冲突的异常编号。
  • 208 是 SQL 中对象名称无效的异常编号。
  • 3960 是快照隔离事务因更新冲突而中止的异常编号。

因此,如果您遇到您所描述的并发问题;请记住,它们将使您的 ISession 无效,并且您必须像上面那样处理它们。

您可能正在寻找的部分内容是 CQRS,其中您有单独的读取和写入端。这可能会有所帮助:http ://abdullin.com/cqrs/,http : //cqrsinfo.com

所以总结一下;您的问题可能与您处理交易的方式有关。另外,尝试跑步select log_wait_reuse_desc from sys.databases where name='MyDBName',看看它会给你带来什么。

于 2010-11-20T19:51:37.373 回答
0

我知道这篇文章是很久以前的,并假设您已修复它,但似乎您与 NHibernate ISession 存在线程共享问题,这不是线程安全的。基本上 1 个线程正在启​​动一个事务,而另一个正在尝试关闭它,从而导致各种混乱。

于 2012-01-27T11:37:24.683 回答