Что касается методологии, я полагаю, что вы лаете не на то б-дерево ;-).
Что мы знаем:
Сначала давайте обобщим и рассмотрим, что мы знаем о ситуации:
Что мы можем догадаться:
Затем мы можем рассмотреть все эти точки данных вместе, чтобы увидеть, сможем ли мы синтезировать дополнительные детали, которые помогут нам найти одну или несколько узких мест, и либо указать на решение, либо, по крайней мере, исключить некоторые возможные решения.
Текущее направление мысли в комментариях заключается в том, что основной проблемой является передача данных между SQL Server и Excel. Это действительно так? Если хранимая процедура вызывается для каждой из 800 000 строк и занимает 50 мс на каждый вызов (то есть на каждую строку), это добавляет до 40000 секунд (не мс). И это эквивалентно 666 минутам (ччмм ;-) или чуть более 11 часов. Все же весь процесс, как говорили, занял всего 7 часов. У нас уже 4 часа больше общего времени, и мы даже добавили время, чтобы выполнить вычисления или сохранить результаты обратно на SQL Server. Так что что-то здесь не так.
Глядя на определение хранимой процедуры, есть только входной параметр для @FileID
; там нет никакого фильтра @RowID
. Поэтому я подозреваю, что происходит один из следующих двух сценариев:
- Эта хранимая процедура фактически вызывается не для каждой строки, а для каждой строки
@FileID
, которая, по-видимому, охватывает приблизительно 4000 строк. Если указанные 4000 возвращенных строк - это достаточно постоянная сумма, то в 800 000 строк есть только 200 из них. И 200 исполнений, каждый из которых занимает 50 мс, составляют всего 10 секунд из этих 7 часов.
- Если эта хранимая процедура действительно вызывается для каждой строки, то первый раз, когда новая
@FileID
передача передается в, занимает немного больше времени, чтобы вытянуть новые строки в буферный пул, но тогда следующие 3999 выполнений обычно возвращаются быстрее из-за того, что уже кэшируется, верно?
Я думаю, что сосредоточение внимания на этой «фильтрующей» хранимой процедуре или любой передаче данных из SQL Server в Excel - это красная сельдь .
На данный момент, я думаю, наиболее важными показателями слабой производительности являются:
- Есть 800 000 строк
- Операция работает по одному ряду за раз
- Данные сохраняются обратно на SQL Server, поэтому «[использует] значения из некоторых столбцов для манипулирования другими столбцами » [моя эм- фаза ;-)]
Я подозреваю, что:
- хотя есть некоторые возможности для улучшения поиска данных и расчетов, их улучшение не приведет к значительному сокращению времени обработки.
- основным узким местом является выдача 800 000 отдельных
UPDATE
заявлений, что составляет 800 000 отдельных транзакций.
Моя рекомендация (на основе имеющейся информации):
Ваша самая большая область усовершенствования должна была бы обновить несколько строк за один раз (то есть в одной транзакции). Вы должны обновить свой процесс, чтобы работать с точки зрения каждого FileID
вместо каждого RowID
. Так:
- прочитать все 4000 строк конкретного
FileID
в массив
- массив должен содержать элементы, представляющие обрабатываемые поля
- цикл по массиву, обрабатывая каждую строку, как вы в настоящее время
- как только все строки в массиве (т.е. для этого конкретного
FileID
) были вычислены:
- начать транзакцию
- вызывать каждое обновление для каждого
RowID
- если нет ошибок, совершите транзакцию
- если произошла ошибка, откатитесь и обработайте соответственно
Если ваш кластеризованный индекс еще не определен как, (FileID, RowID)
то вы должны учитывать это (как предложено @MikaelEriksson в комментарии к Вопросу). Это не поможет этим одиночным ОБНОВЛЕНИЯМ, но, по крайней мере, немного улучшит агрегированные операции, например, то, что вы делаете в этой хранимой процедуре «фильтра», поскольку они все основаны FileID
.
Вы должны рассмотреть возможность перемещения логики на скомпилированный язык. Я бы предложил создать приложение .NET WinForms или даже консольное приложение. Я предпочитаю консольное приложение, так как его легко планировать с помощью агента SQL или запланированных задач Windows. Не должно иметь значения, делается ли это в VB.NET или C #. VB.NET может быть более естественным для вашего разработчика, но все равно будет некоторая кривая обучения.
На данный момент я не вижу причин переходить на SQLCLR. Если алгоритм часто меняется, это будет раздражать, придется постоянно переустанавливать сборку. Перестройка консольного приложения и размещение .exe-файла в соответствующей общей папке в сети, так что вы просто запускаете одну и ту же программу, и она всегда обновляется, должно быть довольно легко сделать.
Я не думаю, что полное перемещение обработки в T-SQL поможет, если проблема в том, что я подозреваю, и вы просто делаете одно ОБНОВЛЕНИЕ за раз.
Если обработка перемещается в .NET, вы можете использовать табличные параметры (TVP), чтобы передать массив в хранимую процедуру, которая будет вызывать UPDATE
метод JOINs для табличной переменной TVP и, следовательно, представляет собой одну транзакцию. , TVP должен быть быстрее, чем 4000 INSERT
с, сгруппированных в одну транзакцию. Но выигрыш от использования TVP более 4000 INSERT
с в 1 транзакции, скорее всего, не будет таким значительным, как улучшение, которое наблюдается при переходе от 800 000 отдельных транзакций к 200 транзакциям по 4000 строк в каждой.
Опция TVP изначально не доступна для VBA, но кто-то предложил обходной путь, который может стоить протестировать:
Как повысить производительность базы данных при переходе с VBA на SQL Server 2008 R2?
ЕСЛИ фильтр proc использует только FileID
в WHERE
предложении, и ЕСЛИ этот proc действительно вызывается для каждой строки, вы можете сэкономить некоторое время обработки, кэшируя результаты первого запуска и используя их для остальных строк FileID
, право?
После того, как вы получите обработку сделаны в FILEID , то мы можем начать говорить о параллельной обработке. Но в этом нет необходимости :). Учитывая, что вы имеете дело с 3 довольно крупными неидеальными частями: транзакции Excel, VBA и 800k, любые разговоры об SSIS или параллелограммы, или кто-то знает, что является преждевременной оптимизацией / типом "корзина перед лошадью" , Если мы сможем сократить этот 7-часовой процесс до 10 или менее минут, подумаете ли вы о дополнительных способах его ускорения? Есть ли у вас запланированное время завершения? Имейте в виду, что как только обработка выполняется для каждого идентификатора файла Таким образом, если бы у вас было консольное приложение VB.NET (то есть из командной строки .EXE), ничто не помешало бы запускать несколько из этих FileID одновременно :), независимо от того, был ли это шаг SQL Agent CmdExec или запланированные задачи Windows, и т.п.
И вы всегда можете использовать «поэтапный» подход и вносить несколько улучшений одновременно. Например, начинать с обновлений по одной FileID
и, следовательно, использовать одну транзакцию для этой группы. Затем посмотрите, сможете ли вы заставить работать TVP. Затем посмотрите, как взять этот код и переместить его в VB.NET (и TVP работают в .NET, поэтому он будет хорошо переноситься).
Что мы не знаем, что еще может помочь:
- Хранимая процедура «фильтра» выполняется для RowID или FileID ? Есть ли у нас полное определение этой хранимой процедуры?
- Полная схема таблицы. Насколько широк этот стол? Сколько существует полей переменной длины? Сколько полей NULLable? Если какие-либо из них NULLable, сколько из них содержат NULL?
- Индексы для этой таблицы. Это разделено? Используется сжатие ROW или PAGE?
- Насколько велика эта таблица в МБ / ГБ?
- Как ведется обслуживание индекса для этой таблицы? Насколько фрагментированы индексы? Насколько актуально обновление статистики?
- Записывают ли какие-либо другие процессы в эту таблицу, пока идет этот 7-часовой процесс? Возможный источник раздора.
- Читают ли какие-либо другие процессы из этой таблицы, пока идет этот 7-часовой процесс? Возможный источник раздора.
ОБНОВЛЕНИЕ 1:
** Кажется, существует некоторая путаница в отношении того, что такое VBA (Visual Basic для приложений) и что с этим можно сделать, так что это просто для того, чтобы убедиться, что мы все на одной веб-странице:
ОБНОВЛЕНИЕ 2:
Еще один момент для рассмотрения: как обрабатываются соединения? Код VBA открывает и закрывает Соединение для каждой операции, или он открывает соединение в начале процесса и закрывает его в конце процесса (т.е. через 7 часов)? Даже с пулом соединений (который по умолчанию должен быть включен для ADO), все равно должно быть значительное влияние между открытием и закрытием один раз, а не открытием и закрытием либо 800 200, либо 1600 000 раз. Эти значения основаны как минимум на 800 000 ОБНОВЛЕНИЙ плюс 200 или 800 КБ EXEC (в зависимости от того, как часто выполняется хранимая процедура фильтра).
Эта проблема слишком большого количества соединений автоматически смягчается рекомендацией, которую я изложил выше. Создавая транзакцию и делая все ОБНОВЛЕНИЯ в этой транзакции, вы будете держать это соединение открытым и повторно использовать его для каждого UPDATE
. Независимо от того, остается ли соединение открытым после начального вызова, чтобы получить 4000 строк для указанного FileID
, или закрыто после этой операции «get» и снова открывается для ОБНОВЛЕНИЙ, это гораздо менее важно, так как мы сейчас говорим о разнице либо Всего 200 или 400 соединений по всему процессу.
ОБНОВЛЕНИЕ 3:
Я сделал несколько быстрых испытаний. Пожалуйста, имейте в виду, что это довольно небольшой тест, а не та же самая операция (чистый INSERT против EXEC + UPDATE). Однако различия во времени, относящиеся к тому, как обрабатываются соединения и транзакции, по-прежнему актуальны, поэтому информацию можно экстраполировать, чтобы иметь здесь сравнительно похожее влияние.
Параметры теста:
- SQL Server 2012 Developer Edition (64-разрядная версия), SP2
Стол:
CREATE TABLE dbo.ManyInserts
(
RowID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
InsertTime DATETIME NOT NULL DEFAULT (GETDATE()),
SomeValue BIGINT NULL
);
Операция:
INSERT INTO dbo.ManyInserts (SomeValue) VALUES ({LoopIndex * 12});
- Всего вставок за каждый тест: 10000
- Сброс для каждого теста:
TRUNCATE TABLE dbo.ManyInserts;
(учитывая природу этого теста, выполнение FREEPROCCACHE, FREESYSTEMCACHE и DROPCLEANBUFFERS, похоже, не принесло особой пользы.)
- Модель восстановления: ПРОСТО (и, возможно, 1 ГБ свободно в файле журнала)
- Тесты, которые используют Транзакции, используют только одно Соединение независимо от того, сколько Транзакций.
Результаты:
Test Milliseconds
------- ------------
10k INSERTs across 10k Connections 3968 - 4163
10k INSERTs across 1 Connection 3466 - 3654
10k INSERTs across 1 Transaction 1074 - 1086
10k INSERTs across 10 Transactions 1095 - 1169
Как вы можете видеть, даже если соединение ADO с БД уже используется всеми операциями, группировка их в пакеты с использованием явной транзакции (объект ADO должен уметь это обрабатывать) гарантированно значительно (т. Е. Улучшение более чем в 2 раза) сократить общее время процесса.