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