Мені доведеться переписати якийсь досить старий код за допомогою команди 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 постійні проміжні таблиці.Запустіть збережену процедуру, яка (a) виконує купу етапів агрегування на 2 таблицях, включаючи кілька
JOIN
умов, а потім (b) виконує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 за пошук найбільш релевантного посилання, але я ціную й інші відповіді. Знову дякую!