我有一个ASP.Net MVC应用程序,它通过NHibernate使用SQL Server 2005。我偶尔会收到以下错误消息:
“System.Data.SqlClient.SqlException(0x80131904):超时已过期。 在完成操作或之前经过的超时时间 服务器没有响应。在 System.Data.SqlClient.SqlConnection.OnError(SqlException异常, Boolean breakConnection)at System.Data.SqlClient.SqlInternalConnection.OnError(SQLEXCEPTION exception,Boolean breakConnection)at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler,SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject stateObj)at System.Data.SqlClient.SqlDataReader.SetMetaData(_SqlMetaDataSet metaData,Boolean moreInfo)at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler,SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject System.Data.SqlClient.SqlDataReader.ConsumeMetaData()中的stateObj) 在System.Data.SqlClient.SqlDataReader.get_MetaData()at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior,String resetOptionsString)at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(的CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,Boolean async)at System.Data.SqlClient.SqlCommand.RunExecuteReader(的CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,String 方法,DbAsyncResult结果)at System.Data.SqlClient.SqlCommand.RunExecuteReader(的CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,String 方法)at System.Data.SqlClient.SqlCommand.ExecuteReader(的CommandBehavior 行为,String方法)at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(的CommandBehavior 行为) System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader()
在NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd)
在NHibernate.Loader.Loader.GetResultSet(IDbCommand st,Boolean autoDiscoverTypes,布尔可调用,RowSelection选择, ISessionImplementor会议) NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters,Boolean returnProxies)at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session,QueryParameters queryParameters,Boolean returnProxies)
在NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)“
大多数情况下,系统运行正常并且反应灵敏。我查看了系统上的锁定,我通常发现有一个查询被另一个查询阻止:
"SELECT TOP 10 d.Id, d.Name FROM Documents d INNER JOIN Users u ON..."
被阻止:
"SELECT TOP 10 Id, Name FROM Users"
查看阻塞语句所持有的锁,在与Users表无关的各种表上有大约十几个独占(X)和(IX)页面和键锁(但这些是被阻止的查询的一部分) )。
这里没有涉及UPDATES,INSERTS或DELETES - 所以为什么一个只读查询会阻塞另一个,为什么在Users表上的简单查询会导致很多其他表的锁定。
答案 0 :(得分:3)
SQL Server问题是数据库上的非默认“隔离级别”设置。 SQL Server具有ReadCommitted的默认隔离级别(锁定策略),这意味着尽快释放读取锁定,但在提交事务之前不会释放写入锁定。在Andy的回答中,默认的隔离级别将导致select被阻止。但是,如果数据库设置为RepeatableRead或Serializable,则选择将始终在事务持续时间内相互阻塞。可以使用此hibernate.cfg.xml配置在NHibernate中控制此设置:
<session-factory>
...
<add key="hibernate.connection.isolation" value="ReadCommitted" />
...
</session-factory>
答案 1 :(得分:2)
这会导致问题:
begin transaction
update Documents
set SomeField = 'SomeValue'
where SomeCondition = 'XXXX'
select top 10 Id, Name from Users
commit
在更新期间在文档表上创建的锁将不会被释放,直到您提交事务为止。这意味着连接可以将select语句作为其最新语句,但仍然保持上次更新的锁定。
一些想法:
请注意,select本身只会生成共享锁。
最后要注意(nolock)锁定提示的想法。阅读未提交的数据很少是一个好主意。