Мне вот-вот придется переписать довольно старый код с помощью команды SQL Server, BULK INSERT
потому что схема изменилась, и мне пришло в голову, что, возможно, мне стоит подумать о переключении на хранимую процедуру с TVP, но мне интересно, какой эффект это могло сказаться на производительности.
Некоторая справочная информация, которая может помочь объяснить, почему я задаю этот вопрос:
Фактически данные поступают через веб-службу. Веб-служба записывает текстовый файл в общую папку на сервере базы данных, который, в свою очередь, выполняет
BULK INSERT
. Этот процесс изначально был реализован в SQL Server 2000, и в то время действительно не было альтернативы, кроме передачи нескольких сотенINSERT
операторов на сервер, что на самом деле было исходным процессом и приводило к катастрофе.Данные массово вставляются в постоянную промежуточную таблицу, а затем объединяются в гораздо большую таблицу (после чего они удаляются из промежуточной таблицы).
Объем данных для вставки «большой», но не «огромный» - обычно несколько сотен строк, в редких случаях может быть до 5-10 тыс. Строк. Поэтому мое шестое чувство, что
BULK INSERT
будучи не-авторизовались операция не будет делать , что большой разницы (но, конечно , я не уверен, отсюда и вопрос).Вставка на самом деле является частью гораздо более крупного конвейерного пакетного процесса и должна происходить много раз подряд; поэтому производительность имеет решающее значение.
Причины, по которым я хотел бы заменить BULK INSERT
TVP:
Написание текстового файла через NetBIOS, вероятно, уже требует некоторого времени, и это довольно ужасно с архитектурной точки зрения.
Я считаю, что промежуточную таблицу можно (и нужно) исключить. Основная причина этого заключается в том, что вставленные данные необходимо использовать для нескольких других обновлений одновременно с вставкой, и гораздо дороже пытаться обновить из массивной производственной таблицы, чем использовать почти пустую промежуточную стол. В TVP параметром в основном является промежуточная таблица, я могу делать с ней все, что захочу, до / после основной вставки.
Я мог бы в значительной степени избавиться от проверки дублирования, кода очистки и всех накладных расходов, связанных с массовыми вставками.
Не нужно беспокоиться о конфликте блокировок в промежуточной таблице или базе данных tempdb, если сервер получает сразу несколько таких транзакций (мы пытаемся этого избежать, но это случается).
Я, очевидно, собираюсь профилировать это, прежде чем что-либо запускать в производство, но я подумал, что было бы неплохо сначала поспрашивать, прежде чем я трачу все это время, посмотреть, есть ли у кого-нибудь строгие предупреждения об использовании TVP для этой цели.
Итак - каков вердикт для всех, кто достаточно знаком с SQL Server 2008, чтобы попытаться или хотя бы исследовать это? Для вставок, скажем, от нескольких сотен до нескольких тысяч строк, происходящих довольно часто, режут ли TVP горчицу? Есть ли существенная разница в производительности по сравнению с объемными вставками?
Обновление: теперь вопросительных знаков на 92% меньше!
(AKA: Результаты испытаний)
Конечный результат находится в производстве после того, что кажется 36-этапным процессом развертывания. Оба решения были тщательно протестированы:
- Извлечение кода из общей папки и
SqlBulkCopy
прямое использование класса; - Переключение на хранимую процедуру с TVP.
Чтобы читатели могли понять, что именно было протестировано, чтобы развеять любые сомнения относительно надежности этих данных, вот более подробное объяснение того, что на самом деле делает этот процесс импорта :
Начните с временной последовательности данных, которая обычно составляет около 20-50 точек данных (хотя иногда может быть несколько сотен);
Проделайте над ним кучу сумасшедших операций, которые в основном не зависят от базы данных. Этот процесс распараллелен, поэтому около 8-10 последовательностей в (1) обрабатываются одновременно. Каждый параллельный процесс генерирует 3 дополнительных последовательности.
Возьмите все 3 последовательности и исходную последовательность и объедините их в пакет.
Объедините пакеты из всех 8-10 завершенных задач обработки в один большой суперпакет.
Импортируйте его, используя либо
BULK INSERT
стратегию (см. Следующий шаг), либо стратегию TVP (переходите к шагу 8).Используйте этот
SqlBulkCopy
класс, чтобы выгрузить весь суперпакет в 4 постоянные промежуточные таблицы.Запустите хранимую процедуру, которая (а) выполняет набор этапов агрегирования для 2 таблиц, включая несколько
JOIN
условий, а затем (б) выполняет операцию дляMERGE
6 производственных таблиц, используя как агрегированные, так и неагрегированные данные. (Законченный)ИЛИ ЖЕ
Сгенерировать 4
DataTable
объекта, содержащих данные, которые нужно объединить; 3 из них содержат типы CLR, которые, к сожалению, не поддерживаются должным образом ADO.NET TVP, поэтому их приходится использовать как строковые представления, что немного снижает производительность.Подайте TVP в хранимую процедуру, которая, по сути, выполняет ту же обработку, что и (7), но напрямую с полученными таблицами. (Законченный)
Результаты были достаточно близкими, но подход TVP в конечном итоге работал лучше в среднем, даже когда данные превышали 1000 строк на небольшой объем.
Обратите внимание, что этот процесс импорта выполняется много тысяч раз подряд, поэтому было очень легко получить среднее время, просто посчитав, сколько часов (да, часов) потребовалось для завершения всех слияний.
Первоначально среднее слияние занимало почти 8 секунд (при нормальной нагрузке). Удаление кладжа NetBIOS и переключение на SqlBulkCopy
сократили время почти до 7 секунд. Переход на TVP еще больше сократил время до 5,2 секунды на пакет. Это на 35% увеличение пропускной способности для процесса, время выполнения которого измеряется часами - это совсем неплохо. Это также улучшение на ~ 25% SqlBulkCopy
.
На самом деле я совершенно уверен, что настоящее улучшение было значительно большим, чем это. Во время тестирования стало очевидно, что окончательное слияние больше не является критическим путем; вместо этого веб-служба, которая выполняла всю обработку данных, начинала сгибаться из-за количества поступающих запросов. Ни ЦП, ни ввод-вывод базы данных не были полностью загружены, и не было значительной активности блокировки. В некоторых случаях мы наблюдали паузу в несколько секунд простоя между последовательными слияниями. Был небольшой промежуток, но намного меньше (полсекунды или около того) при использовании SqlBulkCopy
. Но я полагаю, это станет сказкой на другой день.
Вывод. Табличные параметры действительно работают лучше, чем BULK INSERT
операции для сложных процессов импорта + преобразования, работающих с наборами данных среднего размера.
Я хотел бы добавить еще один момент, чтобы развеять любые опасения со стороны людей, которые выступают за постановочные столы. В некотором смысле, вся эта услуга представляет собой один гигантский промежуточный процесс. Каждый шаг процесса тщательно проверяется, поэтому нам не нужна промежуточная таблица, чтобы определить, почему какое-то конкретное слияние не удалось (хотя на практике этого почти никогда не происходит). Все, что нам нужно сделать, это установить флаг отладки в службе, и он перейдет в отладчик или сбросит свои данные в файл вместо базы данных.
Другими словами, у нас уже более чем достаточно информации о процессе и нам не нужна безопасность промежуточного стола; единственная причина , почему мы имели промежуточную таблицу , в первую очередь, чтобы избежать пробуксовки на всех INSERT
и UPDATE
заявлений , которые мы должны были бы использовать иначе. В исходном процессе промежуточные данные все равно жили в промежуточной таблице лишь доли секунды, поэтому они не добавляли ценности с точки зрения обслуживания / ремонтопригодности.
Также обратите внимание, что мы не заменили каждую BULK INSERT
операцию на TVP. Некоторые операции, которые имеют дело с большими объемами данных и / или не нуждаются в каких-либо специальных действиях с данными, кроме передачи их в БД, все еще используются SqlBulkCopy
. Я не предполагаю, что TVP являются панацеей производительности, только то, что они преуспели SqlBulkCopy
в этом конкретном случае, включающем несколько преобразований между начальным этапом и окончательным слиянием.
Вот и все. За поиск наиболее релевантной ссылки следует обратить внимание на TToni, но я также ценю другие ответы. Еще раз спасибо!