【问题标题】:SqlBulkCopy is slow, doesn't utilize full network speedSqlBulkCopy 很慢,没有充分利用网络速度
【发布时间】:2010-12-15 23:12:15
【问题描述】:

在过去的几周里,我一直在创建能够复制数据库的通用脚本。目标是能够在某个服务器上指定任何数据库并将其复制到其他位置,并且它应该只复制指定的内容。要复制的确切内容在配置文件中指定。该脚本将用于大约 10 个不同的数据库并每周运行一次。最后,我们只复制了大约 3%-20% 的数据库,这些数据库大到 500GB。我一直在使用 SMO 程序集来实现这一点。这是我第一次使用 SMO,我花了一些时间来创建复制模式对象、文件组等的通用方法。 (实际上帮助找到了一些糟糕的存储过程)。

总的来说,我有一个工作脚本缺乏性能(有时会超时),希望你们能提供帮助。当执行 WriteToServer 命令复制大量数据(> 6GB)时,它达到了我的 1 小时超时时间。这里是复制表数据的核心代码。该脚本是用 PowerShell 编写的。

$query = ("SELECT * FROM $selectedTable " + $global:selectiveTables.Get_Item($selectedTable)).Trim()
Write-LogOutput "Copying $selectedTable : '$query'"            
$cmd = New-Object Data.SqlClient.SqlCommand -argumentList $query, $source
$cmd.CommandTimeout = 120;
$bulkData = ([Data.SqlClient.SqlBulkCopy]$destination)
$bulkData.DestinationTableName = $selectedTable;
$bulkData.BulkCopyTimeout = $global:tableCopyDataTimeout # = 3600
$reader = $cmd.ExecuteReader();
$bulkData.WriteToServer($reader); # Takes forever here on large tables

源数据库和目标数据库位于不同的服务器上,因此我也跟踪了网络速度。网络利用率从未超过 1%,这让我感到非常惊讶。但是当我只是在服务器之间传输一些大文件时,网络利用率会飙升至 10%。我尝试将 $bulkData.BatchSize 设置为 5000,但没有真正改变。将 BulkCopyTimeout 增加到更大的数量只会解决超时问题。我真的很想知道为什么网络没有被充分利用。

其他人有这个问题吗?任何有关网络或批量复制的建议将不胜感激。如果您需要更多信息,请告诉我。

谢谢。

更新

我调整了几个提高 SqlBulkCopy 性能的选项,例如将事务日志设置为简单,并为 SqlBulkCopy 提供表锁而不是默认的行锁。此外,某些表针对某些批量大小进行了更好的优化。总体而言,复制的持续时间减少了约 15%。我们要做的是在不同的服务器上同时执行每个数据库的副本。但是我在复制其中一个数据库时仍然遇到超时问题。

当复制一个较大的数据库时,有一个表我一直得到以下异常:

System.Data.SqlClient.SqlException: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding. 

它在开始复制表后大约 16 分钟被抛出,该表不在我的 BulkCopyTimeout 附近。即使我得到了表最终被完全复制的异常。此外,如果我截断该表并仅为该表重新启动我的进程,则这些表将被复制而没有任何问题。但是对于那个表,复制整个数据库的过程总是失败。

在复制那个有问题的表之前,我已经尝试执行整个过程并重置连接,但它仍然出错。我的 SqlBulkCopy 和 Reader 在每个表之后都关闭。关于还有什么可能导致脚本每次都失败的任何建议?

CREATE TABLE [dbo].[badTable](
[someGUID] [uniqueidentifier] NOT NULL,
[xxx] [uniqueidentifier] NULL,
[xxx] [int] NULL,
[xxx] [tinyint] NOT NULL,
[xxx] [datetime] NOT NULL,
[xxx] [datetime] NOT NULL,
[xxx] [datetime] NOT NULL,
[xxx] [datetime] NULL,
[xxx] [uniqueidentifier] NOT NULL,
[xxx] [uniqueidentifier] NULL,
CONSTRAINT [PK_badTable] PRIMARY KEY NONCLUSTERED 
(
[someGUID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

目标数据库上没有此表的索引。

【问题讨论】:

  • 5,000 批量大小似乎太低且无限制太高我建议尝试 50,000 到 100,000 批量大小。
  • 感谢您的意见。我将批量大小设置为 50000,但也没有太大变化。网络利用率仍徘徊在 1%。查看日志文件,复制大表所需的时间仍然相似。实际上,其中一个表的行包含大量内容需要更长的时间。另外,我之前忘了提到这一点,但脚本本身是在创建新数据库的服务器上运行的。
  • 这几乎肯定是目标表上的键/索引设置存在问题。所以请发布您的表和索引定义。
  • 我在复制数据之前停止为表创建索引,现在数据复制过程要快得多。不幸的是,那个表仍然会导致错误。

标签: sql-server database networking smo sqlbulkcopy


【解决方案1】:

您是否考虑过删除索引、进行插入,然后重新建立索引?

【讨论】:

    【解决方案2】:

    我使用了一个数据集,想知道这是否会更快:

    $ds=New-Object system.Data.DataSet
    $da=New-Object system.Data.SqlClient.SqlDataAdapter($cmd)
    [void]$da.fill($ds)
    bulkData.WriteToServer($ds.Tables[0])
    

    【讨论】:

    • 再次感谢您的回复,总体来说这个方法比较慢。它使用了更多的 CPU,但网络利用率有时确实达到了 2.5%。问题是它不是一致的 2.5%,并且在很长一段时间内也为 0(在磁盘写入过程中?)。我可能可以再次使用批量大小,但我想我会改为尝试 bcp 来传输数据。我听说它应该更快。
    • 虽然使用 bcp 开始很好(充分利用网络速度),但 bcp 命令逐渐减速到几乎停止。在复制大表的最后一行时,此 bcp 命令会显着减慢速度,并在新表上重置为全速。我必须忽略 sql 数据库中的某些内容。我听说将表聚集在一起可能会导致此类问题。
    【解决方案3】:

    SqlBulk Copy 是迄今为止将数据复制到 SQL 表中最快的方法。
    您应该获得超过每秒 10,000 行的速度。
    为了测试批量复制功能,请尝试 DBSourceTools。 (http://dbsourcetools.codeplex.com)
    此实用程序旨在将数据库脚本写入磁盘,然后在目标服务器上重新创建它们。
    复制数据时,DBSourceTools 会先将所有数据导出到本地 .xml 文件,然后批量复制到目标数据库。
    这将有助于进一步确定您的瓶颈所在,方法是将流程分为两遍:一遍用于读取,另一遍用于写入。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-28
      • 1970-01-01
      • 2016-01-10
      相关资源
      最近更新 更多