C# 多线程优化性能问题
我有一个巨大的批处理操作,每隔几个月运行一次,解析文本文件并将其导入Sql Server数据库。这个过程需要几天的时间才能完成,我正在寻找加快速度的方法。大约1/3的时间在解析文本,2/3的时间在数据库I/O中 我认为一个简单的解决方案是将这些线程分割成单独的线程。因此,当一个线程写入数据库时,另一个线程可以解析文本。我更改了代码以建立需要执行的SqlCommand对象的列表,解析完成后,这些对象将被传递给新线程执行 在一个小示例中,在一个线程中执行一批SqlCommand对象需要37秒,当我切换到在一个单独的线程中执行这些对象时,我感到惊讶,该过程大大减慢,总共需要63.34秒。我做了一些探索,最终决定在VisualStudio中运行一些性能分析。我在多线程版本上运行了仪器来测量时间,当它以31.04秒的速度运行时,我感到惊讶。我多次重新运行所有测试,结果或多或少相同。因此,与运行性能分析时相比,在工作负载上进行拆分似乎可以提高性能,但如果不运行性能分析,则会降低速度 如果有人能帮我指出是什么原因造成的,我应该在哪里解决它,那就太棒了 这些测试是在运行在6核主机上的四核VMware虚拟机中运行的 编辑:进一步研究后,出现问题的行似乎与解析相关,与DB无关,主要是fileText.Trim()。我不知道为什么在附加了调试器的情况下这些程序会运行得慢得多 代码启动新线程C# 多线程优化性能问题,c#,sql-server,multithreading,performance,C#,Sql Server,Multithreading,Performance,我有一个巨大的批处理操作,每隔几个月运行一次,解析文本文件并将其导入Sql Server数据库。这个过程需要几天的时间才能完成,我正在寻找加快速度的方法。大约1/3的时间在解析文本,2/3的时间在数据库I/O中 我认为一个简单的解决方案是将这些线程分割成单独的线程。因此,当一个线程写入数据库时,另一个线程可以解析文本。我更改了代码以建立需要执行的SqlCommand对象的列表,解析完成后,这些对象将被传递给新线程执行 在一个小示例中,在一个线程中执行一批SqlCommand对象需要37秒,当我切
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();
}
}
}
public void SqlWriterThread(objectcommandbatch)
{
列表批次=(commandBatch作为列表);
使用(SqlConnection=newsqlconnection(HelperDatabase.ConnectionString))
{
connection.Open();
SqlTransaction=connection.BeginTransaction();
尝试
{
foreach(批处理中的SqlCommand cmd)
{
cmd.Connection=连接;
cmd.Transaction=Transaction;
cmd.ExecuteNonQuery();
cmd.Dispose();
}
Commit();
}
抓住
{
transaction.Rollback();
}
}
}
与任何SQL Server性能问题一样,我建议使用方法。这将把问题缩小到实际等待/争用/瓶颈发生的位置
没有任何进一步的数据,并且在您的帖子中缺少任何特定的SQL信息,我们不能说太多:批处理中的那些sqlCommands是什么?是一堆吗?它是一棵树吗?有多少个二级索引?模式的精确定义、精确的数据库文件位置和轴分布,以及基本信息。您正在客户端管理的批处理事务中包装许多命令。他们是什么命令 如果事务是简单的插入,我想知道是否只写一个文件并使用BCP/SSIS,但我认为它要复杂得多
如果是多个父子插入(这就是您使用事务的原因-尽管我看不到这方面的指示,因为您似乎没有为子项创建保存父项ID),这可能是通过一个存储过程的表值参数实现的吗?该存储过程在一次调用中完成整个事务-开始事务插入父级、插入子级、提交事务?您将同步操作的执行分离到异步模式,而其他线程可能同时运行,导致操作执行时间较长 但是,如果将其他部分与线程分开,情况就不会如此,因此在这种情况下,您将从多线程中获得好处。i、 e:一个线程中的“解析文本”,“另一个线程中的数据库i/O”,如果适用,还可以将线程中的工作分离为更多的块“线程” 如果您正在运行
4.0
,我建议您使用Parallel.ForEach
在D.B线程内执行代码:
Parallel.ForEach(batch => cmd
{
cmd.Connection = connection;
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
cmd.Dispose();
});
如果需要几天的时间,您的流程就会固有地中断。您一次处理一个记录吗?尝试对临时表进行大容量插入,然后使用SQl对数据进行扫描,然后使用基于集合的进程插入数据(如果文件很大,您可能希望一次循环数千个批次)
或者创建一个SSIS包来为您进行加载 您正在使用的.net framework版本是什么?当您计时63.34秒时,是否连接了调试器?点击ctrl+f5以在没有调试器的情况下运行,而不是只按f5,这将在连接调试器的情况下运行,这将降低速度performance@BrandonAGr-这似乎解决了问题。如果你把它贴出来作为答案,那么我会把它标记为已回答。这并不能解释为什么在连接了调试器的情况下运行多个线程的速度应该是运行单个线程速度的一半。这才是真正的谜团。DB在SSD上,所以没有主轴。我将以一种有意义的方式找出我可以包含的其他信息。它一次解析一块文本,这不是一个简单的解析过程。然后创建并插入与该文本块相关的所需记录。我一直在进行复杂的转换,但以基于集合的方式进行转换。我在不到一个小时的时间里导入了超过2000万条记录。这不仅仅是导入数据,它还基于数千行代码读取、写入和更新已有的内容