【问题标题】:Distributed transaction with MSMQ and SQL Server but sometimes getting dirty reads使用 MSMQ 和 SQL Server 的分布式事务,但有时会出现脏读
【发布时间】:2016-05-26 14:21:08
【问题描述】:

我们的 SQL Server 2014 数据库设置为 READ_COMMITTED_SNAPSHOT

我们使用 MSMQ 和分布式事务(我们使用 MassTransit 2.10)

在我们系统的一部分中,我们从队列中读取一条消息,进行数据库更新,然后将一条新消息发布到队列中(全部在一个事务下)。

我们发现在处理下一条消息时似乎没有提交更新(它从同一个表中读取第一部分更新),即使我希望该消息仅在队列中同时更新数据库。当我们稍后查询表时,更新的数据按预期存在。这似乎只在我们负载高且很少发生时才会发生。

我们的代码的简化版本

// code that processes message 1
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { Timeout = TimeSpan.FromMinutes(30), IsolationLevel =  IsolationLevel.ReadCommitted }) 
{
     MethodThatUpdatesTableX();
     MethodThatCreatesMessage2();
     scope.Complete();
}    

// message picked up from MSMQ and then (this runs in different thread):
// code that process message 2
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew,  new TransactionOptions() { Timeout = TimeSpan.FromMinutes(30), IsolationLevel = IsolationLevel.ReadCommitted }) 
{
     MethodThatReadsFromTableX(); // so here it seems that changes made in MethodThatUpdatesTableX is sometimes (though rarely) not read
    // other stuff
}    

这是我的理解:

当范围被释放时,对表 X 的更改以及发布到队列的消息都会被提交

MethodThatReadsFromTableX() 从表 XI 中读取时,预计会发生更改(在第一个会话完成之前不应创建会话,因为它无法从队列中获取消息)

我的预期正确吗?可能是什么问题?

【问题讨论】:

    标签: c# sql-server msmq msdtc masstransit


    【解决方案1】:

    以下是我对这个问题的看法。

    在分布式事务中,通常使用两阶段提交。在第一阶段,事务协调员要求每个人准备提交。如果每个人都同意,在第二阶段协调员将命令每个人提交更改。请注意,当节点在此阶段提交更改时 - 它不会再回滚。另请注意,参与者 A(例如 Sql server)和参与者 B (MSMQ) 实际提交更改之间可能存在不可避免的差距。当 MSMQ 已经提交更改时,可能是 SQL Server 还没有提交(尽管已经被命令)。

    因此,当 MSMQ 提交事务时,没有什么可以阻止创建的消息被传递到您的处理器,正如您所说的那样,它在另一个线程中运行。因此,即使在您离开第一个“范围”块之前 - 您的处理器可能会开始处理收到的消息,而 SQL 服务器 可能 仍未提交更改。分布式事务不能阻止这种行为,因为它不能以某种方式强制每个参与者在完全相同的时间完成他们的更改。

    长话短说 - 我认为它按预期工作,您应该做好准备,在处理 MSMQ 消息并实施重试逻辑时可能会丢失所需的数据。

    【讨论】:

      【解决方案2】:

      我会给你一个非常非常简短的答案。

      使用Serializable 作为您的隔离级别。这是保证读者会被作者屏蔽的唯一方法。

      剩下的……

      SQL 非常擅长行级锁定,除非您按顺序插入到聚集索引中。在这种情况下,插入和处理重复键错误会带来性能优势——一个数量级的优势。

      您还需要确保可能会进行脏读的读者也在使用可序列化进行阅读——否则,您将遇到在提交数据库事务之前已使用已发布消息的情况。我已经在大批量生产系统中看到了这种情况,并且发生了这种情况(当然,使用 RabbitMQ 而不是 MSMQ - RabbitMQ 的调度比 MSMQ 快得多)。

      【讨论】:

        猜你喜欢
        • 2018-04-12
        • 2011-09-30
        • 2014-10-07
        • 2015-10-04
        • 2017-04-06
        • 2012-08-14
        • 2015-07-02
        • 2012-02-09
        • 1970-01-01
        相关资源
        最近更新 更多