You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure SQL托管实例与本地SQL中NHibernate超时异常求助

Orchard 1.10.2 + NHibernate 4.0.4 间歇性SQL超时锁站问题排查与版本升级建议

问题描述

使用Orchard框架多年,近期突然出现NHibernate异常,导致站点间歇性锁定,用户无法访问。

当前环境信息

  • Orchard Framework版本:1.10.2
  • NHibernate版本:4.0.4.4000
  • 数据库:Azure SQL托管实例 + 本地SQL Server(未使用Orchard.sdf)
  • 已尝试操作:在托管SQL实例中开启“Read Committed Snapshot”,无改善

疑问

升级NHibernate至新版本、升级Orchard Framework到1.10.3是否能解决问题?目前因预算限制无法迁移至Orchard Core。

异常信息

异常信息:NHibernate.Exceptions.GenericADOException: 无法执行批处理命令。
[SQL: SQL不可用] ---> System.Data.SqlClient.SqlException: 执行超时已过期。
  操作完成前超时时间已过或服务器未响应。
  操作被用户取消。
  语句已终止。 ---> System.ComponentModel.Win32Exception: 等待操作超时
     --- 内部异常堆栈跟踪结束 ---
     在 System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)
     在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
     在 System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
     在 System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
     在 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
     在 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
     在 System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
     在 System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
     在 System.Data.SqlClient.SqlCommandSet.ExecuteNonQuery()
     在 NHibernate.AdoNet.SqlClientBatchingBatcher.DoExecuteBatch(IDbCommand ps)
     --- 内部异常堆栈跟踪结束 ---
     在 NHibernate.AdoNet.SqlClientBatchingBatcher.DoExecuteBatch(IDbCommand ps)
     在 NHibernate.AdoNet.AbstractBatcher.ExecuteBatchWithTiming(IDbCommand ps)
     在 NHibernate.AdoNet.AbstractBatcher.ExecuteBatch()
     在 NHibernate.Engine.ActionQueue.ExecuteActions()
     在 NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
     在 NHibernate.Event.Default.DefaultAutoFlushEventListener.OnAutoFlush(AutoFlushEvent event)
     在 NHibernate.Impl.SessionImpl.AutoFlushIfRequired(ISet1 querySpaces)
     在 NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results)
     在 NHibernate.Impl.CriteriaImpl.List(IList results)
     在 NHibernate.Impl.CriteriaImpl.ListT
     在 Orchard.ContentManagement.DefaultContentQuery.Slice(Int32 skip, Int32 count)
     在 Orchard.ContentManagement.DefaultContentQuery.ContentQuery1.Orchard.ContentManagement.IContentQuery<T>.List()
     在 Orchard.Widgets.Services.DefaultLayerEvaluationService.PopulateActiveLayers()(路径:D:\a\1\s\GMS_Portal\Workforce\KLP_Branch_GMS_Portal\src\Modules\Orchard.Widgets\Services\DefaultLayerEvaluationService.cs:第45行)
     在 Orchard.ContentManagement.Utilities.LazyField1.GetValue()
     在 Orchard.ContentManagement.Utilities.LazyField1.get_Value()
     在 Orchard.Widgets.Services.DefaultLayerEvaluationService.GetActiveLayerIds()(路径:D:\a\1\s\GMS_Portal\Workforce\KLP_Branch_GMS_Portal\src\Modules\Orchard.Widgets\Services\DefaultLayerEvaluationService.cs:第38行)
     在 Orchard.Widgets.Filters.WidgetFilter.OnResultExecuting(ResultExecutingContext filterContext)(路径:D:\a\1\s\GMS_Portal\Workforce\KLP_Branch_GMS_Portal\src\Modules\Orchard.Widgets\Filters\WidgetFilter.cs:第52行)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
     在 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList1 filters, ActionResult actionResult)
     在 System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3_1.b__1(IAsyncResult asyncResult)
已在控制器Tax.Core.Framework.Controllers.HomeController中抛出异常,正在将用户重定向至'~/Login/ApplicationError'

解决方案建议

1. 升级Orchard到1.10.3

Orchard 1.10.3是1.10.x分支的维护版本,针对性修复了多个与当前异常相关的问题:

  • 优化了DefaultLayerEvaluationService中的查询逻辑,减少了不必要的NHibernate自动刷新(AutoFlush)触发,而当前异常正是在该服务执行时触发自动刷新导致批处理超时
  • 修复了会话管理中的竞态条件,降低了站点锁定的概率
  • 优化了与SQL Server(包括Azure SQL)的兼容性,调整了批处理命令的执行逻辑

2. 升级NHibernate版本

当前使用的NHibernate 4.0.4存在已知的SQL Server批处理超时缺陷:

  • 4.0.x版本的SqlClientBatchingBatcher在处理大批次命令时,容易出现超时未重试或连接泄漏问题
  • 建议升级到NHibernate 4.2.x稳定版(Orchard 1.10.x最高兼容此版本),可获得:
    • 批处理超时的重试机制优化
    • 连接池管理的改进
    • 针对Azure SQL的网络超时处理增强

3. 组合升级效果

单独升级某一项可能缓解问题,但同时升级Orchard到1.10.3并将NHibernate升级到4.2.x,解决该类超时锁站问题的概率最高,两者的修复可以形成互补。

临时优化措施(升级前可尝试)

  • 检查Azure SQL托管实例的DTU/CPU使用率,确认是否存在高峰期资源耗尽情况
  • 为Orchard_Widgets_Layers等相关表添加合适的索引,优化PopulateActiveLayers方法中的查询性能
  • 在web.config中调整NHibernate批处理大小:设置hibernate.jdbc.batch_size为较小值(如20),减少单次批处理的命令数量

内容的提问来源于stack exchange,提问作者DrewMadueke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 10:04:50