2011-08-18 46 views
1

我有一个巨大的批处理操作,每隔几个月运行一次,解析并从文本文件导入Sql Server数据库。这个过程需要几天的时间才能完成,我正在研究如何加快它的速度。大约1/3的时间是解析文本,并在数据库I/O中分配2/3的时间。多线程以获得最佳性能问题

我认为一个简单的解决方案是将它们分成单独的线程。所以,当一个线程写入数据库时​​,另一个线程可以解析文本。我改变了代码来建立一个需要被执行的SqlCommand对象列表,然后这些对象被传递给一个新的线程,一旦解析完成就执行。

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

如果有人可以帮助指出可能是什么原因导致这种情况,我应该在哪里寻找解决方法,那太棒了!

测试运行在6核心主机上运行的四核VMware虚拟机上。

编辑:进一步研究了这个问题后,违规行似乎与解析有关,与数据库无关,主要是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(); 
      } 
     } 
    } 
+1

什么是您使用的.net框架版本? –

+0

当你计时63.34秒时,你是否正在运行调试器?点击ctrl + f5运行时没有调试器,只是按下f5,它将连接调试器运行,这会降低性能 – BrandonAGr

+0

@BrandonAGr - 这似乎解决了这个问题。如果您将其作为答案张贴,那么我会将其标记为已回答。这并不能解释为什么在连接调试器的情况下运行多个线程应该以仅运行一个线程的速度运行一半。这是真正的奥秘。 –

回答

1

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

一个人不能说太多没有任何进一步的数据,并缺乏任何特定的SQL信息在你的文章:什么是你的批处理sqlCommands?这是一堆吗?这是一棵树吗?如何二级索引?确切的模式定义,确切的数据库文件位置和主轴分布,你知道,基本信息。

+0

哈哈 - 对于主轴分配:-) – Yahia

+1

数据库在SSD上,所以没有主轴。我会弄清楚我能以有意义的方式包含哪些其他信息。 –

0

您正在将大量命令包装在客户端管理的批处理事务中。他们有什么样的命令?

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

如果它是多个父子插入(这就是为什么你正在使用一个事务 - 虽然我没有看到这一点,因为你似乎没有为儿童创建保存一个父ID),这可能做到了与一个表值参数存储过程,它在一次调用中完成整个事务 - 开始事务插入父项,插入子项,提交事务?

1

你是单独执行异步模式的同步操作,而其他线程可能同时运行导致操作执行时间较长。

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

如果您正在运行4.0我建议你使用Parallel.ForEach用于执行D.B线程里面的代码:如果需要数天

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

Parallel.ForEach不会工作,因为一次只能为每个事务和每个连接执行一个命令。 –

+0

我明白了,你可以使用每个命令的新连接吗? –

1

你的过程本质上是打破。你是否一次录制一张唱片?尝试将批量插入到登台表中,然后使用SQl提取数据,然后使用基于集合的进程插入数据(如果文件很大,您可能希望一次成批循环数千次)。

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

+0

它一次解析1块文本,这不是一个简单的解析过程。然后创建并插入与该文本块相关的所需记录。 –

+0

我一直在做复杂的转换,但是以基于集合的方式来做。我在不到一个小时内导入了超过2000万条记录。 – HLGEM

+0

这不仅仅是导入数据,它是根据创建各种规则的数千行代码来读取,编写和更新已经存在的数据。在此过程中创建了200m +个记录。 –

相关问题