【问题标题】:DataReader Behaviour With SQL Server Locking使用 SQL Server 锁定的 DataReader 行为
【发布时间】:2009-10-08 15:48:53
【问题描述】:

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

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

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

【问题讨论】:

    标签: sql-server locking datareader


    【解决方案1】:
    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 隔离级别?如果是,为什么?
      • 您是否使用锁定提示?如果是,为什么?

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

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

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

    【讨论】:

    • 第 1 点很有趣:我经常看到它
    • 很棒的答案,谢谢。该查询在已提交读隔离级别下执行,并且设置 READ COMMITTED SNAPSHOT ON 确实解决了阻塞问题。我仍然很好奇为什么锁(其中大部分是 S 页锁)在查询期间被持有,因为没有使用锁提示,并且根据 SQL Profiler,没有发生锁升级。跨度>
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2023-04-11
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多