Несколько потоков для оптимальной проблемы производительности - PullRequest
1 голос
/ 18 августа 2011

У меня есть огромная пакетная операция, которая выполняется каждые несколько месяцев при разборе и импорте из текстовых файлов в базу данных Sql Server.Процесс занимает несколько дней, и я ищу способы ускорить его.Примерно 1/3 времени разбора текста и 2/3 времени ввода-вывода базы данных.

Я думал, что простое решение будет разделить их на отдельные потоки.Таким образом, пока один поток пишет в базу данных, другой может анализировать текст.Я изменил код для создания списка объектов SqlCommand, которые должны быть выполнены, после чего они передаются в новый поток для выполнения после завершения синтаксического анализа.

В небольшом примере выполнения пакета объектов SqlCommand водин поток занимает 37 секунд, и я был удивлен, когда переключился на выполнение этих операций в отдельном потоке, что процесс значительно замедлился, что заняло в общей сложности 63,34 секунды.Я немного покопался и в итоге решил провести анализ производительности в Visual Studio.Я запустил Instrumentation для измерения времени в многопоточной версии и был поражен, когда он работал за 31,04 секунды.Я перезапускаю все тесты несколько раз с более или менее одинаковыми результатами.Таким образом, кажется, что при анализе производительности разбивка рабочей нагрузки повышает производительность, но когда анализ производительности не выполняется, он замедляется.

Если кто-то может помочь указать, что может быть причиной этого, и где я должен искать исправлениеэто было бы замечательно!

Тесты выполняются в четырехъядерной виртуальной машине VMware, работающей на 6-ядерном хосте.

edit: если посмотреть дальше, кажется, что ошибочные строките, что связаны с анализом и не имеют ничего общего с БД, в основном fileText.Trim ().Почему они должны работать намного медленнее с подключенным отладчиком, я понятия не имею.

Код запускает новый поток

        while (sqlWriterThread != null && sqlWriterThread.ThreadState == ThreadState.Running)
            Thread.Sleep(0);
        if (sqlWriterThread == null || sqlWriterThread.ThreadState == ThreadState.Stopped)
        {
            sqlWriterThread = new Thread(new ParameterizedThreadStart(SqlWriterThread));
            sqlWriterThread.Name = "SqlWriterThread";
            sqlWriterThread.Priority = ThreadPriority.Highest;
        }
        sqlWriterThread.Start(commandBatch);
        Thread.Sleep(0);

Код выполнения запроса

    public void SqlWriterThread(object commandBatch)
    {
        List<SqlCommand> batch = (commandBatch as List<SqlCommand>);
        using (SqlConnection connection = new SqlConnection(HelperDatabase.ConnectionString))
        {
            connection.Open();
            SqlTransaction transaction = connection.BeginTransaction();
            try
            {
                foreach (SqlCommand cmd in batch)
                {
                    cmd.Connection = connection;
                    cmd.Transaction = transaction;
                    cmd.ExecuteNonQuery();
                    cmd.Dispose();
                }

                transaction.Commit();
            }
            catch
            {
                transaction.Rollback();
            }
        }
    }

Ответы [ 4 ]

1 голос
/ 18 августа 2011

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

Или создайте пакет служб SSIS, чтобы выполнить загрузку за вас.

1 голос
/ 18 августа 2011

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

Однако этого не произойдет, если вы отделите другие части от потоков, поэтому в этом случае вы получите преимущества от многопоточности. то есть: "анализ текста" в потоке, "ввод / вывод базы данных в другом потоке", а также разделение работы внутри потоков на большее количество "потоков" кусков, если это применимо.

Если вы используете 4.0, я предлагаю вам использовать Parallel.ForEach для выполнения кода в потоке D.B:

Parallel.ForEach(batch => cmd
{
    cmd.Connection = connection;
    cmd.Transaction = transaction;
    cmd.ExecuteNonQuery();
    cmd.Dispose();
});
1 голос
/ 18 августа 2011

Как и в случае любой проблемы с производительностью SQL Server, я рекомендую использовать методологию Ожидания и очереди .Это сузит проблему до места фактического ожидания / конфликта / узкого места.

Нельзя много говорить без каких-либо дополнительных данных и отсутствия какой-либо конкретной информации SQL в вашем посте: что это за команды sqlCommandsв ваших партиях?Это куча?Это btree?Как мны вторичные показатели?Точное определение схемы, точное расположение файла базы данных и распределение шпинделей, вы знаете, basic information.

0 голосов
/ 18 августа 2011

Вы включаете несколько команд в пакетную транзакцию, управляемую на стороне клиента. Что это за команды?

Если бы транзакции были простыми вставками, я бы подумал о том, чтобы просто записать файл и использовать BCP / SSIS, но я думаю, что это намного сложнее.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...