在查询执行期间,如果网络缓冲区已满,SQL Server 可以暂停查询。如果客户端没有跟上读取网络的速度,就会发生这种情况,即。它不调用 SqlDataReader.Read()。SQL Server 查询在网络缓冲区被释放时恢复,即。当客户端恢复 SqlDataReader.Read() 时。这意味着当您从数据读取器读取数据集结果时,查询仍在服务器上执行。还有更多细节,比如网络缓冲区的大小、客户端使用 SqlBytes.Stream 的 BLOB 操作等,但这个想法的要点是,慢速客户端会导致查询暂停,当客户端结束时查询结束.
在正常隔离级别(已提交读)下读取数据时,SQL Server 将在它读取的行上放置短暂的共享锁。在更高的隔离级别下,锁的寿命很长,并一直保持到事务结束。
如果不使用任何事务,则每个 SELECT 语句都会在语句执行期间创建一个隐式只读事务。
所以从1、2、3我们可以看出,在高隔离级别下运行查询的慢客户端会导致共享锁被持有很长时间。
现在你需要详细说明你在观察什么:
- 这个查询持有什么样的锁?
- 是否发生锁升级?
- 为什么在查询期间持有锁?
- 您使用 REPEATABLE READ 还是 SERIALIZATION 隔离级别?如果是,为什么?
- 你使用锁定提示吗?如果是,为什么?
您最好的选择可能是快照隔离(至少需要 SQL Server 2005),作为快照隔离级别或作为读取提交的快照。这将完全消除锁定问题,但可能会对 tempdb 产生一些 IO 压力。
其他解决方案是使用游标,但会侵入现有代码库,复杂且仍然容易出错(必须正确获取游标类型)。
顺便说一句,我不建议更改客户端行为。我假设您现在正在 SqlDataReader.Read 循环中在读取业务对象时将它们编组回来,这就是这样做的方法。预先读入内存然后编组可能会在大型数据集上增加更多问题。