SQL Server - 合并大表而不锁定数据
Posted
技术标签:
【中文标题】SQL Server - 合并大表而不锁定数据【英文标题】:SQL Server - Merging large tables without locking the data 【发布时间】:2011-03-18 16:16:41 【问题描述】:我有一组非常大的数据(约 300 万条记录),需要按每日计划与更新和新记录合并。我有一个存储过程,它实际上将记录集分解为 1000 个记录块,并将MERGE
命令与临时表一起使用,以避免在数据更新时锁定活动表。问题是它并不完全有帮助。该表仍然“锁定”,并且我们使用数据的网站在尝试访问数据时会收到超时。我什至尝试将它分成 100 个记录块,甚至尝试使用WAITFOR DELAY '000:00:5'
来查看它是否有助于在合并块之间暂停。它仍然相当缓慢。
我正在寻找有关如何在不锁定表的情况下合并大型数据集的任何建议、最佳做法或示例。
谢谢
【问题讨论】:
有什么方法可以将 NOLOCK 与 MERGE 结合使用吗?数据只从站点读取,只由后端处理器更新,所以我不需要任何锁定。 你有没有办法在 SQL MERGE 语句中不锁定源表? 【参考方案1】:在执行选择时将前端更改为使用 NOLOCK 或 READ UNCOMMITTED。
您不能 NOLOCK MERGE、INSERT 或 UPDATE,因为必须锁定记录才能执行更新。但是,您可以 NOLOCK SELECTS。
请注意,您应该谨慎使用它。如果脏读没问题,那么继续。但是,如果读取需要更新的数据,那么您需要走一条不同的路径,并找出合并 3M 记录会导致问题的确切原因。
我敢打赌,大部分时间都花在了在合并命令期间从磁盘读取数据和/或解决内存不足的情况。最好将更多内存塞入数据库服务器。
理想的数量是有足够的内存来根据需要将整个数据库拉入内存。例如,如果您有一个 4GB 的数据库,那么请确保您有 8GB 的 RAM.. 当然是在 x64 服务器中。
【讨论】:
nolock 不是选择的选项。该代码使用 nhibernate,即使将 nhibernate 设置为不使用锁,它仍然会超时。我们为服务器添加了更多 RAM,并运行了 sql 分析器,它进行了一些增强,据说可以将效率提高 41%。谢谢你的建议,克里斯。 @Josh:使用 NOLOCK 时超时的唯一原因是服务器只是简单地获取资源并且正在执行大量磁盘访问。换句话说,由于内存不足,它是 IO 绑定的。您需要做的三件事是:分析和修复查询、添加 RAM(您已经完成了这些)以及可能更改驱动器以使用更快的东西。但是,最大的影响将来自索引更新和 RAM。 我最终放弃了合并语句,转而支持显式更新和插入。将其与一次拆分 1000 条记录相结合时,它既更快又不会像合并那样锁定页面文件。再次感谢您的建议。他们确实帮助了我们! @Josh:很高兴它帮助您找到解决方案。我将不得不检查我们自己的合并使用情况,看看我们在进行大规模更新时是否有任何类似的问题。我还没有发现任何问题,但我们的一款产品即将扩大 10 倍,因此进行测试可能是个好习惯。 请您确认 - 在 SQL MERGE 语句中,我们可以在源表上使用 NOLOCK 以使源表不被锁定?【参考方案2】:恐怕我有完全相反的经历。我们正在执行更新和插入,其中源表的行数只有目标表的一小部分,目标表有数百万行。
当我们在整个操作窗口中合并源表记录,然后只执行一次 MERGE 时,我们看到性能提高了 500%。我对此的解释是,您只需为 MERGE 命令的前期分析支付一次费用,而不是在一个紧密的循环中一遍又一遍地进行分析。
此外,我确信将 160 万行(源)合并为 700 万行(目标),而不是将 400 行合并为 700 万行,超过 4000 个不同的操作(在我们的例子中)利用 SQL 服务器引擎的功能好多了。同样,相当多的工作是分析这两个数据集,而且只完成一次。
我要问的另一个问题是,您是否知道 MERGE 命令对源表和目标表的索引执行得更好?我想向您推荐以下链接:
http://msdn.microsoft.com/en-us/library/cc879317(v=SQL.100).aspx
【讨论】:
【参考方案3】:从个人经验来看,MERGE 的主要问题是,由于它确实页面锁定,因此它排除了指向表的 INSERT 中的任何并发。因此,如果您沿着这条路走下去,那么您将批处理所有将在单个写入器中命中表的更新是基本的。
例如:我们有一个表,其中 INSERT 每个条目花费了疯狂的 0.2 秒,大部分时间似乎都浪费在事务锁定上,因此我们将其切换为使用 MERGE,一些快速测试表明它允许我们在 0.4 秒内插入 256 个条目,甚至在 0.5 秒内插入 512 个条目,我们使用负载生成器对此进行了测试,一切似乎都很好,直到它投入生产并且所有内容都被页面锁阻塞到地狱,导致总吞吐量远低于使用单独的 INSERT。
解决方案不仅是在 MERGE 操作中对来自单个生产者的条目进行批处理,而且还通过额外级别的队列(以前也是单个连接)在单个 MERGE 操作中将来自生产者的批处理批处理到单个数据库每个数据库,但使用 MARS 交错所有生产者调用存储过程执行实际 MERGE 事务),这样我们就可以毫无问题地处理每秒数千个 INSERT。
在您的所有前端读取中都有 NOLOCK 提示是绝对必须的,永远都是。
【讨论】:
以上是关于SQL Server - 合并大表而不锁定数据的主要内容,如果未能解决你的问题,请参考以下文章