Sql server SQL Server锁定的DataReader行为

Sql server SQL Server锁定的DataReader行为,sql-server,locking,datareader,Sql Server,Locking,Datareader,当通过DataReader从SQL server查询返回大型数据集时,我们的数据层出现了一些问题。当我们使用DataReader来填充业务对象并将它们序列化回客户端时,获取可能需要几分钟(我们正在向用户显示进度:-),但我们发现,受影响的表上存在一些相当核心的锁定,这导致其他更新被阻止 因此,我想我有点天真的问题是,在什么情况下,由于执行查询而取出的锁实际上被放弃了?我们似乎发现锁一直存在,直到处理完DataReader的最后一行,并且DataReader实际关闭为止-这似乎正确吗?简单介绍一下

当通过
DataReader
从SQL server查询返回大型数据集时,我们的数据层出现了一些问题。当我们使用
DataReader
来填充业务对象并将它们序列化回客户端时,获取可能需要几分钟(我们正在向用户显示进度:-),但我们发现,受影响的表上存在一些相当核心的锁定,这导致其他更新被阻止

因此,我想我有点天真的问题是,在什么情况下,由于执行查询而取出的锁实际上被放弃了?我们似乎发现锁一直存在,直到处理完
DataReader
的最后一行,并且
DataReader
实际关闭为止-这似乎正确吗?简单介绍一下
DataReader
在幕后的工作原理会很好,因为我一直在努力寻找关于它的合适信息

我应该说,我意识到锁定问题是主要问题,但我只关心
DataReader
的行为

  • 在执行查询期间,如果网络缓冲区已满,SQL Server可以挂起查询。如果客户端没有跟上读取网络的速度,即没有调用SqlDataReader.Read(),就会发生这种情况。释放网络缓冲区时,即当客户端恢复SqlDataReader.Read()时,SQL Server查询将恢复。这意味着,当您从数据读取器读取数据集结果时,查询仍在服务器上执行。还有更多的细节,比如网络缓冲区的大小、客户端使用SqlBytes.Stream的BLOB操作以及其他,但是这个想法的要点是,速度慢的客户端可能会导致查询暂停,并且在客户端结束时查询结束

  • 在正常隔离级别(read Committed)下读取数据时,SQL Server将在其读取的行上放置短期共享锁。在更高的隔离级别下,锁的寿命很长,并一直保持到事务结束

  • 如果未使用任何事务,则每个SELECT语句将在语句期间创建一个隐式只读事务

  • 因此,从1、2和3可以看出,在高隔离级别下运行查询的缓慢客户端将导致共享锁被长时间持有

    现在,您需要详细说明您观察到的内容:

    • 此查询持有什么类型的锁?
      • S、 U,X
      • 行、页、表
      • 射程锁
    • 是否发生锁升级
    • 为什么在查询期间保持锁?
      • 是否使用可重复读取或序列化隔离级别?若有,原因为何
      • 你使用锁提示吗?若有,原因为何
    您的最佳选择可能是(至少需要SQL Server 2005),或者是作为快照隔离级别,或者是作为读取提交的快照。这将完全消除锁定问题,但可能会对tempdb造成一些IO压力

    另一种解决方案是使用游标,但它对现有的代码库来说非常复杂,而且仍然容易出错(必须正确使用游标类型)


    顺便说一句,我不建议改变客户端的行为。我假设现在您正在SqlDataReader.read循环中读取业务对象时将其封送回,这就是实现方法。预先读取内存,然后封送可能会在大型数据集上增加更多问题。

    选择临时表将缩短锁定时间

    select blah from tbl into #temp << locks held and released
    
    select * from #temp << take all the time you want now
    

    select blah from tbl to#temp Point 1很有趣:我经常看到非常棒的答案,谢谢。查询是在读提交隔离级别下执行的,将读提交快照设置为ON确实解决了阻塞问题。我仍然很好奇,为什么锁(大多数是S页锁)在查询期间一直保持不变,因为没有使用锁提示,而且根据SQL Profiler,没有发生锁升级。