我的C#客户端将批量数据插入SQL Server 2005数据库时遇到了一些性能瓶颈,我正在寻找加快这一过程的方法。

我已经在使用SqlClient.SqlBulkCopy(基于TDS)来加速通过线路的数据传输,这有很大帮助,但我仍然在寻找更多。

我有一个简单的表,看起来像这样:

 CREATE TABLE [BulkData](
 [ContainerId] [int] NOT NULL,
 [BinId] [smallint] NOT NULL,
 [Sequence] [smallint] NOT NULL,
 [ItemId] [int] NOT NULL,
 [Left] [smallint] NOT NULL,
 [Top] [smallint] NOT NULL,
 [Right] [smallint] NOT NULL,
 [Bottom] [smallint] NOT NULL,
 CONSTRAINT [PKBulkData] PRIMARY KEY CLUSTERED 
 (
  [ContainerIdId] ASC,
  [BinId] ASC,
  [Sequence] ASC
))

我在平均大约300行的块中插入数据,其中ContainerId和BinId在每个块中是常量,并且Sequence值是0-n,并且值是基于主键预排序的。

%Disk时间性能计数器花费大量时间在100%,因此很明显磁盘IO是主要问题,但我得到的速度比原始文件副本低几个数量级。

如果我:它有帮助吗?

  1. 我在插入时删除主键,稍后重新创建它
  2. 插入具有相同模式的临时表并定期将它们传输到主表中,以保持表插入发生的大小
  3. 还要别的吗?

- 根据我得到的答复,让我澄清一下:

Portman:我正在使用聚簇索引,因为当数据全部导入时,我需要按顺序依次访问数据。 导入数据时,我并不特别需要索引。 在执行插入时是否有任何优势来使用非聚簇PK索引而不是完全删除约束以进行导入?

Chopeen:数据是在许多其他机器上远程生成的(我的SQL服务器目前只能处理大约10个,但我希望能够添加更多)。 在本地计算机上运行整个过程是不切实际的,因为它必须处理50倍的输入数据才能生成输出。

Jason:我在导入过程中没有对表进行任何并发查询,我会尝试删除主键,看看是否有帮助。

===============>>#1 票数:19

以下是在SQL Server中禁用/启用索引的方法:

--Disable Index ALTER INDEX [IX_Users_UserID] SalesDB.Users DISABLE
GO
--Enable Index ALTER INDEX [IX_Users_UserID] SalesDB.Users REBUILD

以下是一些可帮助您找到解决方案的资源:

一些批量加载速度比较

使用SqlBulkCopy快速将数据从客户端加载到SQL Server

优化批量复制性能

绝对看看NOCHECK和TABLOCK选项:

表提示(Transact-SQL)

INSERT(Transact-SQL)

===============>>#2 票数:18

你已经在使用SqlBulkCopy ,这是一个好的开始。

但是,仅使用SqlBulkCopy类并不一定意味着SQL将执行批量复制。 特别是,SQL Server必须满足一些要求才能执行有效的批量插入。

进一步阅读:

出于好奇,为什么你的指数设置如此? 好像数据筒/ BinId /序列是适合成为一个非聚集索引。 您是否希望将此索引集群化?

===============>>#3 票数:8

我的猜测是,如果将该索引更改为非聚集 ,您将看到显着的改进。 这有两个选择:

  1. 将索引更改为非聚簇,并将其保留为堆表,而不包含聚簇索引
  2. 将索引更改为非聚簇,但随后添加代理键(如“id”)并使其成为标识,主键和聚簇索引

任何一个都可以加快插入速度而不会明显减慢读取速度。

以这种方式考虑 - 现在,你告诉SQL做一个批量插入,但是你要求SQL在你添加任何东西的每个表中重新排序整个表。 使用非聚簇索引,您将按照它们进入的顺序添加记录,然后构建一个指示其所需顺序的单独索引。

===============>>#4 票数:4

你尝试过使用交易吗?

根据您的描述,让服务器将100%的时间提交到磁盘,似乎您在原子SQL语句中发送每行数据,从而迫使服务器提交(写入磁盘)每一行。

如果您使用了事务,那么服务器只会在事务结束时提交一次

如需进一步帮助:您使用什么方法将数据插入服务器? 使用DataAdapter更新DataTable,或使用字符串执行每个句子?

===============>>#5 票数:3

我不是一个聪明的人,我没有很多使用SqlClient.SqlBulkCopy方法的经验,但这是我的2美分它的价值。 我希望它可以帮助你和其他人(或者至少让人们说出我的无知;)。

除非数据库数据文件(mdf)位于与事务日志文件(ldf)不同的物理磁盘上,否则永远不会匹配原始文件复制速度。 此外,任何聚簇索引还需要位于单独的物理磁盘上,以进行更公平的比较。

您的原始副本不会记录或维护选择字段(列)的排序顺序以进行索引。

我同意Portman关于创建非聚簇身份种子并将现有非聚簇索引更改为聚簇索引。

至于您在客户端上使用的构造...(数据适配器,数据集,数据表等)。 如果服务器上的磁盘io为100%,我认为花在分析客户端构造上的时间最好,因为它们似乎比服务器当前处理的速度快。

如果你按照波特曼关于最小日志记录的链接,我不会认为在交易中包围你的批量副本会有很大帮助,但是我生命中多次出错了;)

这对您现在不一定有帮助,但如果您弄清楚当前的问题,下一条评论可能有助于解决下一个瓶颈(网络吞吐量) - 特别是如果它是通过互联网...

Chopeen也问了一个有趣的问题。 您是如何确定使用300记录计数块插入的? SQL Server有一个默认的数据包大小(我相信它是4096字节),我有必要派生你的记录大小,并确保你有效地利用客户端和服务器之间传输的数据包。 (注意,您可以在客户端代码上更改数据包大小,而不是服务器选项,这显然会改变所有服务器通信 - 可能不是一个好主意。)例如,如果您的记录大小导致300个记录批次需要4500字节,你将发送2个数据包,第二个数据包大多是浪费。 如果批量记录计数是任意分配的,那么做一些快速简单的数学计算可能是有意义的。

根据我的判断(并记住数据类型大小),每个记录只有20个字节(如果int = 4个字节,smallint = 2个字节)。 如果您使用300个记录计数批次,那么您尝试发送300 x 20 = 6,000个字节(加上我猜测连接的一点开销等)。 您可能更有效率地以200个记录计数批次(200 x 20 = 4,000 +空间开销)= 1个数据包发送这些数据。 然后,你的瓶颈似乎仍然是服务器的磁盘io。

我意识到你正在使用相同的硬件/配置将原始数据传输与SqlBulkCopy进行比较,但如果挑战是我的话,我也会去那里:

这篇文章可能不会帮助你了,因为它已经相当陈旧但我接下来会问你的磁盘的RAID配置是什么以及你使用的磁盘速度是多少? 尝试将日志文件放在数据文件中使用RAID 10且RAID 5(理想情况为1)的驱动器上。 这可以帮助减少大量主轴移动到磁盘上的不同扇区,并导致更多时间读/写而不是非生产性“移动”状态。 如果您已经分离了数据和日志文件,那么您的索引是否与数据文件中的其他物理磁盘驱动器有关(您只能对聚簇索引执行此操作)。 这将不仅允许通过数据插入同时更新日志信息,而且允许索引插入(以及任何昂贵的索引页操作)同时发生。

===============>>#6 票数:3

BCP - 设置起来很痛苦,但是自从数据库出现以来它一直存在,而且非常快。

除非您按顺序插入数据,否则3部分索引会使事情变慢。 稍后应用它也会使事情变得缓慢,但将会迈出第二步。

Sql中的复合键总是很慢,键越大越慢。

===============>>#7 票数:1

我认为这听起来可以使用SSIS包来完成。 它们与SQL 2000的DTS包类似。 我已经用它们成功地转换了纯文本CSV文件,现有SQL表,甚至跨越多个工作表的6位数行的XLS文件。 您可以使用C#将数据转换为可导入格式(CSV,XLS等),然后让SQL服务器运行计划的SSIS作业以导入数据。

创建一个SSIS包非常容易,SQL Server的企业管理器工具内置了一个向导(我认为标记为“导入数据”),在向导结束时,它为您提供了将其保存为SSIS包的选项。 Technet上还有更多信息。

===============>>#8 票数:0

是的,你的想法会有所帮助
如果在加载时没有读取,则在选项1上倾斜。
如果在处理过程中正在查询目标表,请依赖选项2。

@安德鲁
题。 你插入300块。你插入的总金额是多少? SQL服务器应该能够非常快速地处理300个普通的旧插件。

  ask by Andrew translate from so

未解决问题?本站智能推荐:

1回复

使用C#将插入内容推入SQL Server?

我想一次向SQL Server中插入大约5000行(通常可以是10K +)。 我的插入语句是由C#程序生成的。 一种方法是简单地生成一个包含这些SQL语句的大字符串,然后实际上使SQL Server使用SqlCommand.ExecuteNonQuery();执行它们SqlCommand
4回复

SQL Server中与C#中的string.format类似的功能

在C#中,如果我这样做: 其结果将是: C007 SQL Server中是否有功能可以执行相同的操作? 还是我必须自己创建一个函数?
1回复

C#与SQL Server 2008复制'\\'[关闭]

运行SQL命令时出现问题,结果是哈希密码为sha 256 数据库中的密码: "Z?VU??u2???f?[??\\n?Mn??=1???<3?\\v?" c#在查询后返回的密码: "Z?VU??u2???f?[??\\ \\n?Mn??=1???<3?\\ \\v?"
4回复

使用SQL Server的C#.net Winform应用程序中的事件处理

我有一个带有SQLuser列表的组合框,这些列表具有事先授予的权限。 具有username , permission_name列的表PERMISSION_DETAIL包含授予或拒绝的权限的详细信息 现在,当用户从组合框中选择一个SQLuser时,权限将显示为一个复选框,例如是否已授予权限
5回复

VB或C#for SQL Server-仅学习SQL Server(来自Access)

我刚刚接触SQL Server,并且对MS Access和Visual Basic(无论如何在Access中都可以使用VB)都有一定的经验。 因此,我尝试获取材料(书籍等)以学习如何对sql server进行编程,并且我想知道是否应该使用C#路由而不是Visual Basic for sq
3回复

将数据从一个表分发到SQL Server中的多个表

我有3张桌子: Item_Detail ----------------- ID,名称 ItemPurchased_Detail --- QtyPurchased,RateOfPurchase,DiscountReceived ItemSold_Detail ---------
1回复

高效的SQL查询批量数据

我正在寻找这样的东西: 找到n个连续的免费电话,但不完全是这个。 作为性能调整的一部分,我正在寻找一种更好的解决方案,当我单击数据网格的页面索引时,该解决方案将仅从SQL过程返回前10条记录。 总记录数将超过10万,但我不想每次都处理整个记录集。 即。 当我单击pageindex
3回复

从C#.NET Winform应用程序重新定位SQL Server中的查询

我想运行相同的查询十次。 我可以使用GO,但是我想从我的.net winform应用程序中执行此操作。 当用户单击按钮时,查询将被执行并将10行插入表ITEMS中。 有什么解决方案呢?
1回复

如何在安装了SQL Express 2012但仍保留为SQL Express 2005的C#中附加mdf文件?

我有一个使用SQL Server 2005 Express .mdf文件的Windows应用程序,并且该计算机上已经有许多客户端以及该应用程序和SQL Server 2005 Express版本。 现在,我正在开发机中进行测试,但是使用SQL Server 2012 Express,我的问
1回复

如果数据库排序规则不是“ SQL_Latin1_General_CP1_CI_AS”,则会出现C#错误“找不到存储过程”

这是我遇到的情况: A)C#代码:执行存储过程“ Check_Data” 我想指出的是,这里的C#代码没有任何问题,因为它尽可能地基本,而且我和我的同事也对其进行了双重检查。 B)SQL Server: 所有数据库均为MSSQL2005。 请不要建议升级它们