【问题标题】:TransactionScope() and parallel query executionTransactionScope() 和并行查询执行
【发布时间】:2022-08-04 00:05:46
【问题描述】:

我们正在尝试在事务范围内运行并行查询以提高代码的性能。我们要在数据库中进行一些相互没有连接的更改。我们可以像这样运行代码:

using(var tran = new System.Transactions.TransactionScope())
{
    await queryMethod1Async();
    await queryMethod2Async();
    await queryMethod3Async();

    tran.Complete();
}

然而,由于这些方法是相互独立的,我们希望像这样运行代码:

using(var tran = new System.Transactions.TransactionScope())
{
    var tasks = new List<Task>();

    tasks.Add(queryMethod1Async());
    tasks.Add(queryMethod2Async());
    tasks.Add(queryMethod3Async());

    await Task.WhenAll(tasks);

    tran.Complete();
}

我们在并行执行中遇到了一些问题:

  • 并行运行查询似乎会升级事务。在升级过程中,有时会出现错误:
The wait operation timed out  --> There is already an open DataReader associated with this Command which must be closed first.    
at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)     
at System.Data.SqlClient.SqlDelegatedTransaction.Promote() --> Failure while attempting to promote transaction.    
at System.Data.SqlClient.SqlDelegatedTransaction.Promote()     
at System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx)     
at System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx) --> The transaction has aborted.    
at System.Transactions.TransactionStateAborted.CheckForFinishedTransaction(InternalTransaction tx)     
at System.Transactions.Transaction.Promote()     
at System.Transactions.TransactionInterop.ConvertToOletxTransaction(Transaction transaction)    
at System.Transactions.TransactionInterop.GetExportCookie(Transaction transaction, Byte[] whereabouts)     
at System.Data.SqlClient.SqlInternalConnection.GetTransactionCookie(Transaction transaction, Byte[] whereAbouts)     
at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)     
at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)     
at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)   

经过一番调查,这似乎是因为在升级期间,使用了事务的原始连接,但在并行执行期间,该连接可能正在使用中。 我尝试启用MARS 以避免此问题,但这会导致不同的错误:

Current Microsoft Distributed Transaction Coordinator (MS DTC) transaction must be committed by remote client.    
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)     
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)     
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)     
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)     
at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)     
at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)     
at System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment enlistment) --> The transaction has aborted.    

澄清一下,上述每个方法都创建了一个新的sqlconnection,并且我们已经正确配置了MSDTC

我不确定为什么会发生第二个错误,但我有一种感觉,我正在以错误的方式解决这个问题。是否可以在事务范围内执行并行查询,如果可以,正确的方法是什么?

  • 恕我直言,正确的措辞应该是\“同时查询执行\",因为 TransactionScope()-context-manager 的作用域最终注入了一个 \"macro\"-structure 屏障,让所有 async-\"just\"-[CONCURRENT] 任务坐下来等待最慢,在代码执行只能稍后离开范围之前。这种手动同步屏障主要是异步的,“只是”-[CONCURRENT] 独立操作,否则不协调的任务不是上下文的属性+不是 True-[ PARALLEL] 问题,只是一个 \"exit-ALAP barrier\" 强制等待直到所有 \"just\"-[CONCURRENT] 流程完成

标签: c# sql-server parallel-processing transactions


【解决方案1】:

是否可以在事务范围内执行并行查询,如果可以,正确的方法是什么?

是的。您必须为每个查询使用单独的 SqlConnection,并正确配置和运行 MSDTC。但这样做很少有用,因为每个命令都可以在 SQL Server 上并行运行,因此同时运行多个命令通常不会减少整体运行时间。

【讨论】:

  • 我们已经为每个查询创建了一个单独的 SqlConnection,并且 MSDTC 配置正确,但我们仍然收到上述错误。我也不同意它很少有用的说法。我知道每个命令都可以在 sql server 上并行运行,但是当上一个命令完成时,下一个命令被发送到 sql server 时,什么都不会并行运行
  • 那好吧应该工作,但 MSTDC 可能很难解决。您可能会考虑使用 3 个单独的事务并在它们全部完成后将它们全部提交。
  • 拥有 3 个单独的事务违背了使用事务的目的。我需要完成所有操作,或者一个都不做。但我仍然可以向您保证,我们已正确配置 MSDTC。我们正在为数百名客户运行企业 SaaS 环境,并且已经使用 msdtc 十年了
  • 单独事务的想法是,当您尝试提交 Sql Server 事务时,它不太可能失败(除非您使用的是内存中 OLTP)。任何失败都应该在任何尝试提交之前发生,并及时回滚所有事务。因此,如果 MTDTC 正常工作,您能否将代码简化为复制代码并将其添加到您的问题中?
  • 我现在理解您的想法,但我是否正确理解您的结论是不应该在单个事务中进行并行查询?如果这不受“支持”,我会感到非常惊讶,我需要完全更改我的代码设置以运行并行事务来完成此操作。这将使代码更加复杂且难以理解/维护
【解决方案2】:

我认为最好在数据库中创建运行所有查询的过程。 然后您可以使用数据集在一个请求中获取它。 现在在数据集中使用每个数据表。

【讨论】:

    【解决方案3】:

    您可以尝试几个选项:

    1. 启用 MARS 后,使用 TransactionScopeAsyncFlowOption.Enabled 参数创建事务范围:
      using(var tran = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)
      {...
      

      我使用带有 dapper 的 System.Data.SqlClient.SqlConnection 来读取/写入数据并且它有效。

      1. 如果您使用的是 Entity Framework Core,您可以使用 SemaphoreSlim 同步访问您的 DbContext 以避免此问题。例子:
      // declare semaphore
      var syncSemaphore = new SemaphoreSlim(1);
      // [...]
      // and use it in all your async functions like this:
      async Task queryMethod1Async()
      {
          await syncSemaphore.WaitAsync();
          try
          {
             // Use your database connection - read or write and release semaphore so other tasks can access
          }
          finally
          {
              syncSemaphore.Release();
          }            
      }
      

    【讨论】:

    • 我们已经启用了 asyncflow 选项,并且我们所有使用实体框架的查询都使用它们自己的 dbcontext 实例。但是在这个例子中,事务中的查询没有使用实体框架
    【解决方案4】:

    您的任何queryMethodXAsync 是否会产生它自己的事务(作为嵌套事务)并且在您尝试完成TransactionScope 之前不会回滚或提交它

    如果您调用在其中使用事务并且没有以正确方式处理它的存储过程,则可能会发生类似的事情。

    您的异常中有更多详细信息的 InnerExcpetion 吗?

    【讨论】:

      【解决方案5】:

      你解决了这个问题吗?我也有同样的情况,还没有找到合适的解决方案。我的情况类似:

      我有 -

                 using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                 {
                     var task1 = _repository1.SaveAsync(dto);
                     var task2 = _repository2.SaveAsync(dto1, false);
      
                     await Task.WhenAll(task1 , task2).ConfigureAwait(false);
      
                     transaction.Complete();
                 }
      

      有时它会失败:

         System.Data.SqlClient.SqlException: Current Microsoft Distributed    Transaction Coordinator (MS DTC) transaction must be committed by remote client.
      
      

      MSDTC 配置正确,并且为 MSDTC 和 RPC 配置了防火墙

      有什么建议吗?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-26
        • 1970-01-01
        相关资源
        最近更新 更多