2010-03-16 73 views
16

我有得到这样的SQL服务器插入性能

INSERT INTO InvoiceDetail (LegacyId,InvoiceId,DetailTypeId,Fee,FeeTax,Investigatorid,SalespersonId,CreateDate,CreatedById,IsChargeBack,Expense,RepoAgentId,PayeeName,ExpensePaymentId,AdjustDetailId) 
VALUES(1,1,2,1500.0000,0.0000,163,1002,'11/30/2001 12:00:00 AM',1116,0,550.0000,850,NULL,@ExpensePay1,NULL); 
DECLARE @InvDetail1 INT; SET @InvDetail1 = (SELECT @@IDENTITY); 

只有110K行生成此查询生成一个INSERT查询。

它需要30分钟,所有这些查询的来执行

我检查查询计划和最大的%节点

聚集索引插入,在57%的查询成本 有着悠久的XML我不想发布。

表阀芯是38%的查询费用

<RelOp AvgRowSize="35" EstimateCPU="5.01038E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Eager Spool" NodeId="80" Parallel="false" PhysicalOp="Table Spool" EstimatedTotalSubtreeCost="0.0466109"> 
    <OutputList> 
    <ColumnReference Database="[SkipPro]" Schema="[dbo]" Table="[InvoiceDetail]" Column="InvoiceId" /> 
    <ColumnReference Database="[SkipPro]" Schema="[dbo]" Table="[InvoiceDetail]" Column="InvestigatorId" /> 
    <ColumnReference Column="Expr1054" /> 
    <ColumnReference Column="Expr1055" /> 
    </OutputList> 
    <Spool PrimaryNodeId="3" /> 
</RelOp> 

所以我的问题是什么,是没有办法,我能做些什么来改善这个东西的速度?我已经运行 ALTER TABLE TABLENAME NOCHECK约束全部 查询前然后 ALTER TABLE TABLENAME NOCHECK约束查询后全部为 。

而这并没有削减几乎任何时间。

知道我在使用SqlCommand对象发送查询的.NET应用程序中运行这些查询。

然后我试着将sql命令输出到一个文件,然后使用sqlcmd执行它,但我没有得到任何更新,所以我放弃了这一点。

任何想法或提示或帮助?

更新:

好,所以你们都非常有帮助。在这种情况下,我希望我可以赞扬不止一个答案。

解决此问题的解决方案有两方面。

第一:

1)我禁用/重新启用所有的外键(比投下他们更容易)

ALTER TABLE TableName NOCHECK CONSTRAINT ALL 
ALTER TABLE TableName CHECK CONSTRAINT ALL 

2)我禁用/重新启用索引(再次比下降容易得多)

ALTER INDEX [IX_InvoiceDetail_1] ON [dbo].[InvoiceDetail] DISABLE 
ALTER INDEX [IX_InvoiceDetail_1] ON [dbo].[InvoiceDetail] REBUILD PARTITION = ALL WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, ONLINE = OFF, SORT_IN_TEMPDB = OFF) 

第二种:

我包裹所有插入件统计的进入一项交易。我最初并不知道如何在.NET中这样做。

我非常感谢所有的输入。

如果我曾经做过这种从DB到DB的翻译,我一定会从BULK INSERT开始。它似乎更灵活,更快。

+0

两者告诉我们更多关于您的聚集索引和插入。您的插入是否与您的聚集索引相关,以便所有记录将被追加到现有记录之后? (即记录自然是顺序的聚集索引) – 2010-03-16 20:27:29

+5

永远不要使用@@身份! YOu可能会让数据完整性大打折扣,因为它不会总是返回正确的值。改用scope_Identity()。 – HLGEM 2010-03-18 18:42:23

+1

我知道这是旧的,但你记得最后的查询花了多少时间?只是想知道这个差异有多大? – 2017-01-13 07:39:52

回答

11

听起来像插入正在导致SQL Server重新计算索引。一个可能解决方案将删除索引,执行插入,并重新添加索引。有了您的尝试解决方案,即使您告诉它忽略约束,它仍然需要保持索引更新。

5

您已将此问题标记为“bulkinsert”。那么为什么不使用BULK INSERT命令呢?

如果您需要进度更新,您可以将批量插入拆分为更小的块,并在每个块完成后更新进度。

+0

好的其他建议。 – Jaxidian 2010-03-16 20:27:17

+0

批量插入是否允许您输入标识列?我必须承认我不太了解批量插入,但我正在研究它。 – Jose 2010-03-16 20:32:06

+0

@Jose:我认为这篇文章回答你的问题:http://msdn.microsoft.com/en-us/library/ms186335.aspx – 2010-03-16 20:38:11

4

有一个几件事情可以做:

1) Disable any triggers on this table 
2) Drop all indexes 
3) Drop all foreign keys 
4) Disable any check constraints 
1

嗯,让它运行,检查性能计数器。你看到了什么?你有什么光盘布局?准确地说,我可以在30分钟内插入几百万行 - 近1亿行(实时财务信息,链接到其他3张表)。我敢打赌,你的IO布局是糟糕的(即坏的磁盘结构,坏的文件分布)

10

你是否从.Net客户端一次执行这些查询(即发送110,000个单独的查询请求到SQL Server)?

在这种情况下,很可能是网络延迟以及将这些INSERT发送到SQL Server而不进行批处理的其他开销,而不是SQL Server本身。

查看BULK INSERT。

3

运行单独的INSERT总是最慢的选项。另外 - 与@@ IDENTITY的交易是什么 - 看起来并不像你想跟踪那些介于两者之间的那些。

如果您不想使用文件或SSIS中的BULK INSERT,则有一个SqlBulkCopy feature in ADO.NET,如果您绝对必须从.NET程序中执行此操作,那么这可能是您最好的选择。

与我研究和写这个答案相比,11万行应该花费更少的时间导入。

+2

SqlBulkCopy +1 - 这也是一个很好的建议。 – 2010-03-16 20:57:17

7

很可能这是commit flush wait。如果你没有将INSERT集合包装到显式管理的事务中,那么每个INSERT都是它自己的自动提交事务。含义是每个INSERT自动发出一个提交,并且提交必须等到该日志持久(即写入磁盘)。每次插入后冲洗日志非常缓慢。

例如,尝试插入100K行就像在一个单行你犯风格:

set nocount on; 
declare @start datetime = getutcdate(); 

declare @i int = 0; 
while @i < 100000 
begin 
INSERT INTO InvoiceDetail (
    LegacyId,InvoiceId,DetailTypeId,Fee, 
    FeeTax,Investigatorid,SalespersonId, 
    CreateDate,CreatedById,IsChargeBack, 
    Expense,RepoAgentId,PayeeName,ExpensePaymentId, 
    AdjustDetailId) 
    VALUES(1,1,2,1500.0000,0.0000,163,1002, 
    '11/30/2001 12:00:00 AM', 
    1116,0,550.0000,850,NULL,1,NULL); 
    set @i = @i+1; 
end 

select datediff(ms, @start, getutcdate()); 

这个运行在我的服务器上约12秒。但是,增加的事务管理,并承诺每1000行的100K行插入仅持续约4秒:

set nocount on; 
declare @start datetime = getutcdate(); 

declare @i int = 0; 
begin transaction 
while @i < 100000 
begin 
INSERT INTO InvoiceDetail (
    LegacyId,InvoiceId,DetailTypeId, 
    Fee,FeeTax,Investigatorid, 
    SalespersonId,CreateDate,CreatedById, 
    IsChargeBack,Expense,RepoAgentId, 
    PayeeName,ExpensePaymentId,AdjustDetailId) 
    VALUES(1,1,2,1500.0000,0.0000,163,1002, 
    '11/30/2001 12:00:00 AM', 
    1116,0,550.0000,850,NULL,1,NULL); 
    set @i = @i+1; 
    if (@i%1000 = 0) 
    begin 
    commit 
    begin transaction 
    end 
end 
commit; 
select datediff(ms, @start, getutcdate()); 

而且因为我可以在12秒内插入10万行甚至W/O批量提交,而你需要30分钟,它值得研究1)IO子系统的速度(例如你在驱动器上看到的是什么Avg. Sec per Transaction)以及2)客户端代码在从一次调用中获取@@标识和调用下一个插入之间做了什么?这可能是大部分时间都在堆栈的客户端。一个简单的解决方案是并行启动多个插入(BeginExecuteNonQuery),这样您就可以持续地插入SQL Server插入。

+0

您的负载完全在数据库服务器上运行。我认为他一次只能在电线上插入一张插页,这就是为什么他们很慢。可能更多地是由于网络延迟和往返次数超过实际提交刷新开销。 – 2013-06-11 04:09:00

+0

我刚刚运行了我认为是110K插入语句的一个合理的再现,一个接一个地在电线上,是的,开始一个事务首先增加吞吐量一个数量级! – 2015-09-25 19:50:26

+0

@RalphShillington在SQL Server 2014及更高版本中,您还可以选择[懒惰耐久性](https://msdn.microsoft.com/zh-cn/library/dn449490.aspx)。 – 2015-09-25 20:09:45

3

提高插入性能的一些建议:

  • 增加ADO.NET BATCHSIZE
  • 明智地选择目标表的聚簇索引,以便插入不会导致聚集索引节点分裂(如AUTOINC列)
  • 插入到一个临时堆表,然后再发出一个大的“插入按选择”语句所有的临时表的数据推到实际的目标表
  • 应用SqlBulkCopy的
  • 放置表锁之前插入(如果你的业务场景允许的话)

Tips For Lightning-Fast Insert Performance On SqlServer