Сервер C # + Sql - Выполнение хранимой процедуры большое количество раз. Лучший способ? - PullRequest
4 голосов
/ 29 июня 2009

У меня есть одна хранимая процедура, которая вставляет данные в 3 таблицы (делает UPSERTS) и имеет некоторую рудиментарную логику. (IF-THEN-ELSE)

Мне нужно выполнить этот Sproc миллионы раз (из приложения C #), используя разные параметры, и мне нужно, чтобы он был БЫСТРОМ.

Каков наилучший способ сделать это?

Кто-нибудь знает с открытым исходным кодом (или нет) готовый индексатор документов, кроме Lucene или Sql Server FTS ?? * Я пытаюсь создать словесный указатель документа. Для каждого слова в документе я вставляю в БД слово, docID и положение слова.

Это происходит, например, 100000 раз для 100 документов.

Sproc : есть 3 таблицы для вставки, для каждой из которых я делаю UPSERT.

Приложение C # :

using (SqlConnection con = new SqlConnection(_connectionString))
            {
                con.Open();
                SqlTransaction trans = con.BeginTransaction();
                SqlCommand command = new SqlCommand("add_word", con, trans);
                command.CommandType = System.Data.CommandType.StoredProcedure;
                string[] TextArray;
                for (int i = 0; i < Document.NumberOfFields; i++)
                {
                  ...
                 Addword(..., command);  <---- this updates parameters with new values and ExecuteNonQuery.
                }

            }

Забыл упомянуть, этот код вызывает взаимные блокировки в Sql Server. Я понятия не имею, почему это происходит.

Ответы [ 10 ]

3 голосов
/ 29 июня 2009
  1. Удалите все индексы в таблицах, которые вы загружаете, затем добавьте их обратно после завершения загрузки. Это предотвратит много перебора / переиндексации за каждое изменение.

  2. Убедитесь, что база данных выделила достаточно физического файлового пространства до загрузки, чтобы ей не приходилось тратить время на постоянный захват ее из файловой системы при загрузке. Обычно при заполнении базы данных увеличиваются примерно на 10%, и в этот момент сервер sql блокирует запросы, пока не будет выделено больше места. При загрузке объема данных, о которых вы говорите, sql придется делать много блокировок.

  3. По возможности просматривайте массовую загрузку / массовую копию.

  4. Выполните всю свою, ЕСЛИ ЕЩЕ, логику в коде. Просто отправьте фактические значения, которые вы хотите сохранить, в s'proc, когда он будет готов. Вы могли бы даже запустить два потока. Один для оценки данных и их постановки в очередь, другой для записи очереди на сервер БД.

  5. Посмотрите на программы Off The Shelf, которые делают именно то, о чем вы говорите, с индексацией документов. Скорее всего, они решили эти проблемы.

  6. Избавьтесь от требований Транзакции, если это возможно. Постарайтесь сделать вызовы s'proc максимально простыми.

  7. Посмотрите, сможете ли вы ограничить количество слов, которые вы храните. Например, если вас не интересуют слова «it», «as», «I» и т. Д., Отфильтруйте их ПЕРЕД вызовом s'proc.

2 голосов
/ 29 июня 2009

Если вы хотите быстро объединить данные INSERT из C #, проверьте класс SqlBulkCopy (.NET 2.0 и выше).

1 голос
/ 29 июня 2009

Попробуйте использовать XML для этого.

Вам просто нужно будет выполнить 1 раз:

Пример:

DECLARE @XMLDoc XML
SET @XMLDoc = '<words><word>test</word><word>test2</word></words>'

CREATE PROCEDURE add_words
(
    @XMLDoc XML
)
AS

DECLARE @handle INT

EXEC sp_xml_preparedocument @handle OUTPUT, @XMLDoc

INSERT INTO TestTable
SELECT * FROM OPENXML (@handle, '/words', 2) WITH 
  (
    word varchar(100)
  )
EXEC sp_xml_removedocument @handle
1 голос
/ 29 июня 2009

Это может показаться рудиментарным подходом, но он должен работать и быть быстрым. Вы можете просто создать огромный текстовый файл со списком операторов SQL, а затем запустить его из командной строки. Если я не ошибаюсь, должна быть возможность пакетировать команды с помощью оператора GO. Кроме того, вы можете сделать это прямо из своего приложения, объединяя несколько команд SQL в виде строк и выполняя их в пакетном режиме. Кажется, что вы пытаетесь сделать это разовая задача, и что данные не поступают непосредственно как ввод данных пользователя. Так что вы должны быть в состоянии справиться с побегом самостоятельно.

Я уверен, что есть более изощренные способы сделать это (SqlBulkCopy выглядит как хорошее начало), поэтому, пожалуйста, рассмотрите это как предложение. Я бы потратил некоторое время на выяснение того, существуют ли вначале более элегантные способы, которые лучше.

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

1 голос
/ 29 июня 2009

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

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

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

Другим распространенным решением для этого является использование SqlServer Bulk операций.

Чтобы вернуться к хранимой процедуре, имейте в виду, что оптимизация вашей T-SQL и db-схемы (с индексами и т. Д.) Может оказать великолепное влияние на вашу производительность.

0 голосов
/ 29 июня 2009

Чтобы переместить много данных на сервер, используйте либо SqlBulkCopy, либо параметр с табличным значением, если вы используете 2008. Если вам нужна скорость, не выполняйте хранимую процедуру один раз в строке, разработайте набор на основе набора, который обрабатывает все (или большая партия) строк.

0 голосов
/ 29 июня 2009

Предполагая, что это приложение, в котором между несколькими пользователями не будет конфликтов, попробуйте этот подход:

  • Вставьте ваши параметры в таблицу, настроенную для этой цели
  • Измените ваш SP, чтобы проходить по этой таблице и выполнять свою работу над каждой строкой
  • Вызовите ИП один раз
  • Пусть SP обрежет таблицу входных данных, когда она будет завершена

Это исключит накладные расходы на вызов SP миллионами раз, и вставки параметров в таблицу могут быть объединены ("INSERT INTO foo (v) VALUE ('bar'); INSERT INTO foo (v) VALUE) ('bar2'); INSERT INTO foo (v) VALUE ('bar3'); ").

Недостаток: выполнение SP займет много времени, и о прогрессе не будет никакой обратной связи, что не очень удобно для пользователя.

0 голосов
/ 29 июня 2009

В этом случае связь с базой данных, вероятно, станет узким местом, особенно если база данных находится на другом компьютере. Я предлагаю отправить весь документ в базу данных и написать sproc, который разбивает его на слова, или использовать управляемый код, размещенный на сервере sql.

0 голосов
/ 29 июня 2009

Если вы пытаетесь оптимизировать скорость, попробуйте просто обновить оборудование SQL Server. Установка некоторой оперативной памяти и высокоскоростного RAID на вашем сервере может быть наиболее экономичным долгосрочным решением для ускорения вашей скорости запросов. Оборудование относительно дешевое по сравнению со временем разработчика.

Прислушайтесь к словам Джеффа Этвуда:

Ужасы кодирования: аппаратные средства дешевы, программисты дорогие

0 голосов
/ 29 июня 2009

- отредактировано с момента редактирования вопроса.

Самая большая проблема - убедиться, что сохраненный процесс правильно настроен. Ваш код C # работает примерно так же быстро, как вы собираетесь его получить.

...