SQL Server锁定的DataReader行为

时间:2009-10-08 15:48:54

标签: sql-server locking datareader

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

所以我想我的稍微天真的问题是,由于执行查询而被取出的锁实际上放弃了什么?我们似乎发现锁已经存在,直到DataReader的最后一行被处理并且DataReader实际上已经关闭 - 这看起来是否正确?关于DataReader如何在幕后工作的快速101将是伟大的,因为我努力寻找任何体面的信息。

我应该说我发现锁定问题是主要问题,但我只是关注DataReader的行为。

2 个答案:

答案 0 :(得分:11)

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

  2. 在正常隔离级别(读取提交)下读取数据时,SQL Server将在其读取的行上放置短暂的共享锁。在更高的隔离级别下,锁是长期存在的,直到交易结束。

  3. 如果没有使用任何事务,则每个SELECT语句将在语句的持续时间内创建隐式只读转换。

  4. 因此,从1,2和3我们可以看到在高隔离级别下运行查询的慢客户端会导致共享锁被保留很长时间。

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

    • 此查询包含哪种锁?
      • S,U,X?
      • 行,页,表?
      • 范围锁?
    • 是否发生锁定升级?
    • 为什么是在查询期间持有的锁?
      • 您是否使用REPEATABLE READ或SERIALIZATION隔离级别?如果是,为什么?
      • 你使用锁定提示吗?如果是,为什么?

    您最好的选择可能是snapshot isolation(至少需要SQL Server 2005),可以是快照隔离级别,也可以是读取提交的快照。这将完全消除锁定问题,但会在tempdb上产生一些IO压力。

    其他解决方案是使用游标,但是对现有的代码库是复杂的,并且仍然容易出错(必须使光标类型正确)。

    顺便说一句,我建议更改客户端行为。我现在假设你在SqlDataReader循环中读取它们时正在编组业务对象,这就是这样做的方法。预先读取内存然后编组可能会在大型数据集上添加更多问题。

答案 1 :(得分:1)

选择临时表将减少锁定持续时间

select blah from tbl into #temp << locks held and released

select * from #temp << take all the time you want now