4

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

所以我想我有点幼稚的问题是,在什么时候因为执行查询而被取出的锁实际上被放弃了?我们似乎发现锁一直保留到最后一行DataReader被处理并且DataReader实际关闭 - 这看起来正确吗?一个关于DataReader幕后工作的快速 101 会很棒,因为我一直在努力寻找任何像样的信息。

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

4

2 回答 2

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

  2. 在正常隔离级别(已提交读)下读取数据时,SQL Server 将在它读取的行上放置短暂的共享锁。在更高的隔离级别下,锁的寿命很长,并一直保持到事务结束。

  3. 如果不使用任何事务,则每个 SELECT 语句都会在语句执行期间创建一个隐式只读事务。

所以从1、2、3我们可以看出,在高隔离级别下运行查询的慢客户端会导致共享锁被持有很长时间。

现在你需要详细说明你在观察什么:

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

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

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

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

于 2009-10-08T16:37:05.107 回答
1

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

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

select * from #temp << take all the time you want now
于 2010-06-14T11:38:20.133 回答