2016-06-30 51 views
0

我正在处理一个涉及处理大量文本文件并导致将记录插入到mssql数据库或更新现有信息的项目。并行处理多个sql语句导致死锁

sql语句被写入并存储在一个列表中,直到文件完成处理。 然后处理该列表。每条语句都是一次处理一条语句,但这可能会导致数千条语句,并且可能会创建一个长时间运行的流程。

要尝试加快这个过程中,我介绍了一些并行处理,但这种偶尔会导致以下错误:

Transaction (Process ID 94) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

代码如下:

public static void ParallelNonScalarExecution(List<string> Statements, string conn) 
    { 
     ParallelOptions po = new ParallelOptions(); 
     po.MaxDegreeOfParallelism = 8; 
     CancellationTokenSource cancelToken = new CancellationTokenSource(); 
     po.CancellationToken = cancelToken.Token; 
     Parallel.ForEach(Statements, po, Statement => 
     { 
      using (SqlConnection mySqlConnection = new SqlConnection(conn)) 
      { 
       mySqlConnection.Open(); 
       using (SqlCommand mySqlCommand = new SqlCommand(Statement, mySqlConnection)) 
       { 
        mySqlCommand.CommandTimeout = Timeout; 
        mySqlCommand.ExecuteScalar(); 
       }    
      } 
     }); 
    } 

更新语句我认为很简单在他们试图实现什么:

UPDATE TableA SET Notes = 'blahblahblah' WHERE Code = 1 
UPDATE TableA SET Notes = 'blahblahblah', Date = '2016-01-01' WHERE Code = 2 
UPDATE TableA SET Notes = 'blahblahblah' WHERE Code = 3 
UPDATE TableA SET Notes = 'blahblahblah' WHERE Code = 4 
UPDATE TableB SET Type = 1 WHERE Code = 100 
UPDATE TableA SET Notes = 'blahblahblah', Date = '2016-01-01' WHERE Code = 5 
UPDATE TableB SET Type = 1 WHERE Code = 101 

什么解决这个问题的最好方法是什么?

+0

用'profiler'捕获死锁图。而这个sql代码演示了单个更新语句还是这个并行单行语句执行的列表? –

+0

也许你应该重新设计你在做什么。看起来你正在创建一个语句列表,然后试图执行它们,每个语句打开一个新的连接?你为什么想这么做。将文本文件加载到SQL服务器上的临时表中,然后执行简单的服务器端插入/更新(可能是单个插入和单个更新)可能会更容易,更快速。 –

+0

@CetinBasoz我相信我需要每个线程一个连接。我以前打开过一个连接,但遇到了如下所述的命令问题:[link](http://stackoverflow.com/questions/18475195/error-there-is-already-an-open-datareader-associated-with -this-command-which-mu) – AdamB

回答

0

从我看到你不想做你正在做的事情。我不建议有多个更新语句影响不同线程上的相同数据/表。这是种族条件/死锁的滋生。在你的情况下,它应该是安全的,但如果在任何时候你改变了哪里的条件和有重叠,你就会遇到一个竞争条件问题。

如果你真的想用多线程加快速度,而不是让一个线程上的tableA和一个线程上的tableB上的所有更新语句都具有更新语句。另一个想法是阻止你的更新声明。

UPDATE TableA SET Notes = 'blahblahblah' WHERE Code IN (1,2,3,4,5) 
UPDATE TableA SET Date = '2016-01-01' WHERE Code IN (2,5) 
UPDATE TableB SET Type = 1 WHERE Code IN (100,101) 

上面的语句应该能够在concurent环境中独立执行,因为没有两个语句会影响同一列吗?

0

线程A更新资源X并且不提交并且可以继续执行更多更新。 线程B更新资源y并且不提交,并且可以继续执行更多更新。 此时,两者都有未提交的更新。

现在线程A更新资源y并等待来自线程B的锁。 线程B不被任何东西阻挡,因此它继续,最终尝试更新资源x并被锁A阻塞x 。 现在他们陷入僵局。这是一个僵局,没有人可以继续犯下,所以系统杀死一个。 为了减少死锁的可能性(但这并不能完全消除这种可能性),您必须更频繁地提交,否则您必须仔细订购更新,以便在继续执行任何更新之前完成对x的所有更新并完成年。