多线程可实现最佳性能问题

时间:2011-08-18 16:55:23

标签: c# sql-server multithreading performance

我有一个庞大的批处理操作,每隔几个月运行一次解析并从文本文件导入到Sql Server数据库。这个过程需要几天时间才能完成,我正在寻找加快速度的方法。大约1/3的时间是解析文本,2/3的时间是数据库I / O.

我认为一个简单的解决方案是将它们分成不同的线程。因此,当一个线程正在写入数据库时​​,另一个线程可以解析文本。我更改了代码以构建需要执行的SqlCommand对象列表,然后在解析完成后将这些对象传递给新线程执行。

在一个小样本中,在一个线程中执行一批SqlCommand对象需要37秒,当我在一个单独的线程中切换到执行这些时,我感到很惊讶,这个过程大量减速,总共花费63.34秒。我做了一些探索,最终决定在Visual Studio中运行一些性能分析。我运行Instrumentation来测量多线程版本的时序,当它在31.04秒运行时很惊讶。我重复所有测试几次,或多或少相同的结果。因此,与运行性能分析时相比,工作负载的分割可以提高性能,但是当不运行性能分析时,速度就会降低。

如果有人可以帮助说明可能导致这种情况的原因以及我应该在哪里修复它会非常棒!

测试正在一个运行在6核主机上的四核VMware虚拟机中运行。

编辑:进一步研究了这些违规行似乎是与解析有关的行,而与DB无关,主要是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 个答案:

答案 0 :(得分:1)

与任何SQL Server性能问题一样,我建议使用Waits and Queues方法。这会将问题缩小到实际发生等待/争用/瓶颈的地方。

对于任何进一步的数据,你的帖子中缺少任何特定的SQL信息,你不能说太多了:批量中的那些sqlCommands是什么?它是堆吗?它是btree吗?如何mny二级索引?模式的精确定义,确切的数据库文件位置和主轴分发,您知道,基本信息。

答案 1 :(得分:1)

您将同步操作的执行分开到异步模式,而其他线程可能同时运行,导致操作在更长的时间内执行。

但是,如果您将其他部分与线程分开,则不会出现这种情况,因此在这种情况下您将从多线程中获益。即:在一个线程中“解析文本”,“另一个线程中的数据库I / O”,并且如果适用,还将线程内的工作分成更多块“线程”。

如果您正在运行4.0我建议您使用Parallel.ForEach来执行D.B线程中的代码:

Parallel.ForEach(batch => cmd
{
    cmd.Connection = connection;
    cmd.Transaction = transaction;
    cmd.ExecuteNonQuery();
    cmd.Dispose();
});

答案 2 :(得分:1)

如果需要数天,您的流程本身就会被破坏。你一次是在录制一张唱片吗?尝试批量插入到临时表,然后使用SQl来提升数据,然后使用基于集合的进程来插入数据(如果文件很大,您可能希望一次批量循环几次)。

或者创建一个SSIS包来为您进行加载。

答案 3 :(得分:0)

您在客户端管理的批处理事务中包装了许多命令。他们是什么样的命令?

如果交易是简单的插入,我想知道只是编写一个文件并使用BCP / SSIS,但我想它比这复杂得多。

如果它是多个父子插入(这就是你使用事务的原因 - 虽然我没有看到这个,因为你似乎没有为子项创建保存父ID),这可能是用存储过程的表值参数,它在单个调用中执行整个事务 - 开始事务插入父项,插入子项,提交事务?