Лучшая производительность импорта на больших наборах данных - PullRequest
0 голосов
/ 25 августа 2011

У меня есть несколько заданий, которые вставляют большие наборы данных из текстового файла. Данные загружаются через .NET SqlBulkCopy.

В настоящее время я загружаю все данные во временную таблицу и затем вставляю их в рабочую таблицу. Это было улучшение по сравнению с прямым импортом в производство. Запрос результатов вставки T-SQL был намного быстрее. Данные загружаются только этим методом, других вставок или удалений нет.

Однако я вернулся к тайм-аутам из-за блокировок во время выполнения задания. Задание состоит из следующих этапов:

  1. загрузка данных во временную таблицу
  2. начать транзакцию
  3. удалить текущие и будущие строки с датами
  4. вставка из временной таблицы
  5. совершить

Это происходит один раз в час. Эта часть занимает 70 секунд. Мне нужно получить это как можно меньшее число.

Производственная таблица содержит около 20 миллионов записей, а каждый импорт занимает около 70 тыс. Строк. К таблице нет доступа ночью, поэтому я использую это время для выполнения всех необходимых работ по обслуживанию (перестройка статистики, индекса и т. Д.). Из 70K добавлено, что ~ 4K сохраняется изо дня в день, то есть таблица увеличивается на 4K в день.

Я думаю, решение из 2 частей:

Задание превратится в задание на копирование / переименование. Я вставляю все текущие данные во временную таблицу, создаю статистику и индекс, переименовываю таблицы, удаляю старую таблицу.

Создать таблицу истории, чтобы разбить более старые данные. «Текущая» таблица будет содержать данные за 6 месяцев, около 990 тыс. Записей. Это сделает таблицу удаления / вставки меньше и, надеюсь, более производительной. Я бы предпочел не делать этого; таблица хорошо спроектирована с идеальными показателями; запросы очень быстро. Но в конце концов это может потребоваться.

Редактировать: Использование Windows 2003, SQL Server 2008

Мысли? Другие предложения?

Ответы [ 6 ]

0 голосов
/ 07 ноября 2011

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

0 голосов
/ 25 августа 2011

Получите лучшее оборудование.Используя 3 потока, 35 000 партий элементов, я импортирую около 90 000 элементов в секунду, используя этот подход.

Извините, но в какой-то момент аппаратное обеспечение определяет скорость вставки.Важно: SSD для логов, зеркальный;)

0 голосов
/ 25 августа 2011

Еще один трюк, который вы можете использовать, - иметь дельта-таблицу для обновлений.У вас будет 2 таблицы с видом на них, чтобы объединить их.Одна таблица, TableBig, будет содержать старые данные, а вторая таблица, TableDelta, будет содержать дельты, которые вы добавляете в строки в tableBig.

Вы создаете представление над ними, которое их складывает.Простой пример:

Например, ваши старые данные в TableBig (20M строк, много индексов и т. Д.)

ID   Col1  Col2
123     5    10
124     6    20

И вы хотите обновить 1 строку и добавить новую, чтобыпосле этого таблица выглядит следующим образом:

ID   Col1  Col2
123     5    10  -- unchanged
124     8    30  -- this row is updated
125     9    60  -- this one added

Затем в TableDelta вы вставляете эти две строки:

ID   Col1  Col2
124     2    10  -- these will be added to give the right number
125     9    60  -- this row is new

, и вид равен

select ID, 
       sum(col1) col1, -- the old value and delta added to give the correct value
       sum(col2) col2
from   (
           select id, col1, col2 from TableBig
           union all 
           select id, col1, col2 from TableDelta
       )
group by ID

Ночью.вы можете объединить TableDelta с TableBig и индексировать и т. д.

Таким образом, вы можете полностью оставить большую таблицу в одиночестве в течение дня, и TableDelta не будет иметь много строк, так что общий запрос будет очень хорошим.Получение данных из BigTable выгодно благодаря отсутствию, получение строк из DeltaTable не представляет никакой проблемы, поскольку оно мало, их суммирование обходится дешевле по сравнению с поиском данных на диске.Загрузка данных в TableDelta очень дешева, потому что вы можете просто вставить в конце.

С уважением, Герт-Ян

Обновление для текстовых столбцов:

Вы можете попробовать что-то подобное с двумя таблицами, но вместо добавления вы быаналог:

Select isnull(b.ID,   o.ID)   Id, 
       isnull(b.Col1, o.Col1) Col1  
       isnull(b.Col2, o.Col2) col2
From   TableBig b
       full join TableOverWrite o on b.ID = o.ID

Основная идея та же: большая таблица с индексами и маленькая таблица для обновлений, которые в них не нуждаются.

0 голосов
/ 25 августа 2011

Вы должны изучить использование секционированных таблиц. Основная идея заключается в том, что вы можете загрузить все свои новые данные в новую таблицу, а затем соединить эту таблицу с оригиналом в качестве нового раздела. Это на несколько порядков быстрее, чем вставка в текущую существующую таблицу.

Позже вы можете объединить несколько разделов в один больший раздел.

Больше информации здесь: http://msdn.microsoft.com/en-us/library/ms191160.aspx

0 голосов
/ 25 августа 2011

Хорошо, один действительно хитрый способ - переименовать текущую таблицу в TableA и создать вторую таблицу с такой же структурой, как TableB, и с теми же данными.Затем настройте представление с тем же именем и точными полями в TableA.Теперь весь ваш существующий код будет использовать представление вместо текущей таблицы.Представление начинается с просмотра таблицы A.

В процессе загрузки загрузите в TableB.Обновите определение представления, изменив его, чтобы посмотреть на TableB.Ваши пользователи не работают менее секунды.Затем загрузите те же данные в TableA и сохраните, с какой таблицы вы должны начать где-нибудь в таблице базы данных.В следующий раз сначала загрузите TableA, затем измените представление так, чтобы оно указывало на TableA, затем перезагрузите TableB.

0 голосов
/ 25 августа 2011

Ответ должен заключаться в том, что ваши запросы, считываемые из этой таблицы, ДОЛЖНЫ БЫТЬ ПРОЧИТАННЫМИ, поскольку загрузка данных является единственным местом, которое изменяет данные. С READ UNCOMMITTED запросы SELECT не получат блокировки.

http://msdn.microsoft.com/en-us/library/ms173763.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...