2010-03-16 7 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 строк.

Это займет 30 минут для всех этих запросов, чтобы выполнить

Я проверил план запроса и крупнейшие% узлы

индекса кластерных вставок на 57% запросы стоили , который имеет длинный XML, что Я не хочу публиковать сообщения.

A Таблица золотника, который является 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 СООТВЕТСТВУЕТ ВСЕ после запросов.

И это не сбрило что-нибудь в стороне от времени.

Знаю, что я запускаю эти запросы в .NET-приложении, которое использует объект SqlCommand для отправки запроса.

Затем я попытался вывести команды sql в файл, а затем выполнить его с помощью sqlcmd, но я не получал никаких обновлений о том, как это происходит, поэтому я отказался от этого.

Любые идеи или намеки или помощь?

UPDATE:

Ok так вы все были очень полезны. В этой ситуации мне хотелось бы дать ответ на более чем один ответ.

Решение исправить это было двояким.

Первые:

1) I-инвалидов/снова включить все внешние ключи (намного проще, чем сбросив их)

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

2) I-инвалидов/снова включить индексы (опять-таки гораздо проще, чем падение)

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.

Я очень ценю весь вход, который я получил.

Если я когда-либо делаю такой перевод из БД в БД, я обязательно начну с BULK INSERT. Это кажется намного более гибким и быстрым.

+0

Расскажите нам о вашем кластерном индексе и вставки. Связана ли ваша вставка с вашим кластеризованным индексом, чтобы все записи были добавлены после существующих записей? (т. е. записи естественно последовательны для кластерного индекса) –

+5

Никогда не используйте @@ Identity! С этим вы можете испортить целостность данных, поскольку оно не всегда возвращает правильное значение. Вместо этого используйте scope_Identity(). – HLGEM

+1

Я знаю, что это уже давно, но помните, сколько времени занимали последние вопросы? Просто интересно, какая разница, все это сделано? –

ответ

11

Похоже, что вставки заставляют SQL Server пересчитывать индексы. Одним из возможных является решение - отказаться от индекса, выполнить вставку и повторно добавить индекс. При попытке вашего решения, даже если вы скажете ему игнорировать ограничения, ему все равно нужно будет обновлять индекс.

5

Вы отметили этот вопрос как «bulkinsert». Так почему бы не использовать команду BULK INSERT?

Если вы хотите обновить прогресс, вы можете разделить объемную вставку на более мелкие части и обновить прогресс после завершения каждой части.

+0

Хорошее другое предложение. – Jaxidian

+0

Входит ли вставка Bulk для ввода столбцов идентификации? Должен признаться, я не слишком много знаю о массовой вставке, но я ее изучаю. – Jose

+0

@Jose: Я думаю, эта статья ответит на ваш вопрос: http://msdn.microsoft.com/en-us/library/ms186335.aspx –

4

Есть еще несколько вещей, которые вы можете сделать:

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

Хм, дайте ему поработать, проверить счетчики производительности. что ты видишь? Какая дисковая компоновка у вас есть? Я могу вставить миллион строк за 30 минут - точнее, сто миллионов миллиметров строк (финансовая информация в реальном времени, ссылки на 3 другие таблицы). Я довольно уверен, что ваш формат ввода-вывода плох (т. Е. Плохая структура диска, плохой дистрибутив файлов)

10

Выполняете эти запросы по одному от клиента. Net (т. Е. Отправляете 110 000 запросов отдельных запросов на SQL Server)?

В этом случае, скорее всего, это сетевая латентность и другие накладные расходы для отправки этих ВСТАВКИ на SQL Server без их пакетной обработки, а не непосредственно с SQL Server.

Проверьте БОЛЬШОЙ ВСТАВКИ.

3

Выполнение отдельных ВСТАВКИ всегда будет самым медленным вариантом. Также - что такое сделка с @@ IDENTITY - не похоже на то, что вы следили за теми, кто находится между ними.

Если вы не хотите использовать BULK INSERT из файла или SSIS, есть SqlBulkCopy feature in ADO.NET, который, вероятно, будет вашим лучшим выбором, если вам абсолютно необходимо сделать это из .NET-программы.

Строки 110k должны занимать меньше времени для импорта, чем я, переустановки и написания этого ответа.

+2

+1 для SqlBulkCopy - это тоже хорошее предложение. –

7

Скорее всего, это фиксация заподлицо. Если вы не переносите наборы 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()); 

Это работает примерно 12seconds на моем сервере. Но добавление управления транзакциями и совершая каждые 1000 строк вставку 100к строк длится всего около 4s:

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()); 

Кроме того, учитывая, что я могу вставить 100k строк в 12 секунд, даже без партии фиксации, в то время как вам нужно 30 минут , стоит оценить 1) скорость вашей подсистемы ввода-вывода (например, то, что вы видите на дисках Avg. Sec per Transaction) и 2) что еще делает код клиента между получением идентификатора @@ с одного вызова и вызовом следующей вставки. Может быть, основная часть времени находится на стороне клиента в стеке. Одним простым решением было бы запустить несколько вставок параллельно (BeginExecuteNonQuery), чтобы вы постоянно загружали вставки SQL Server.

+0

Ваша загрузка выполняется полностью на сервере базы данных. Я думаю, что он посылает свои вставки по одному по проводам, и поэтому они медленны. Вероятно, больше из-за латентности сети и круглых поездок, чем фактические накладные расходы на флеш. –

+0

Я просто управлял тем, что, по моему мнению, является разумным воспроизведением его заявлений о вставке 110 тыс., Один за другим, по проводам, и, да, начало транзакции сначала увеличивает пропускную способность на порядок! –

+0

@RalphShillington с SQL Server 2014, а позже у вас также есть альтернатива [lazy durability] (https://msdn.microsoft.com/en-us/library/dn449490.aspx). –

3

Некоторые предложения для повышения производительности вставки:

  • Увеличить ADO.NET BATCHSIZE
  • Выберите кластерный индекс целевой таблицы мудро, так что вставки не приведет к кластерным узлов разделений индекса (например, AutoInc колонка)
  • вставить во временную таблицу кучи, а затем выдать один большой «вставить-на-выберите» заявление, чтобы подтолкнуть все, что данные промежуточной таблицы в фактической целевой таблице
  • Применить SqlBulkCopy
  • Поместите блокировку таблицы перед вставкой (если ваш бизнес-сценарий позволяет ей)

Взятые из Tips For Lightning-Fast Insert Performance On SqlServer

Смежные вопросы