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
相关产品推荐
相关产品推荐

