并行处理多个sql语句会导致死锁

本文关键字:死锁 语句 sql 并行处理 | 更新日期: 2023-09-27 17:53:23

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

sql语句被写入并存储在一个列表中,直到文件被处理完。然后处理这个列表。每条语句一次处理一条,但这可能是数千条语句,并可能创建一个非常长的运行过程。

为了加速这个过程,我引入了一些并行处理,但这偶尔会导致以下错误:

事务(进程ID 94)在lock |通信上死锁缓冲区资源与另一个进程并已选择为死锁的受害者。重新运行事务。

代码如下:

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语句的目的很简单:

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

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

并行处理多个sql语句会导致死锁

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

如果你真的想用多线程来加快这个速度,而不是把所有的tableA的更新语句放在一个线程上,把所有tableB的更新语句放在一个线程上。另一个方法是阻塞你的update语句。

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)

上述语句应该能够在并发环境中独立执行,因为没有两条语句影响同一列。

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

现在线程A更新资源y并等待线程B的锁。线程B没有被任何东西阻塞,所以它继续前进,最终尝试更新资源x,并被A对x的锁阻塞。现在他们陷入了僵局。这是一个僵局,没有人可以继续提交,所以系统杀死了一个。您必须更频繁地提交以减少死锁的可能性(但这并不能完全消除这种可能性),或者您必须仔细地安排更新的顺序,以便在对y进行任何更新之前完成对x的所有更新。