简体   繁体   English

MySQL的TransactionScope和分布式事务

[英]TransactionScope with MySQL and Distributed Transactions

I have an ASP.Net WebAPI instance setup that uses a MySQL database for storage. 我有一个使用MySQL数据库进行存储的ASP.Net WebAPI实例设置。 I have written an ActionFilter that handles creating a TransactionScope for the lifetime of a single endpoint request. 我编写了一个ActionFilter ,用于处理在单个端点请求的生存ActionFilter创建TransactionScope的问题。

public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
    HttpActionContext actionContext,
    CancellationToken cancellationToken,
    Func<Task<HttpResponseMessage>> continuation)
{
    var transactionScopeOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted };
    using (var transaction = new TransactionScope(TransactionScopeOption.RequiresNew, transactionScopeOptions, TransactionScopeAsyncFlowOption.Enabled))
    {
        var handledTask = await continuation();

        transaction.Complete();

        return handledTask;
    }
}

Then throughout the endpoints I have different queries/commands that open/close connections using the autoenlist=true functionality of DbConnection 's. 然后在整个端点上,我有不同的查询/命令,它们使用DbConnectionautoenlist=true功能打开/关闭连接。 An example endpoint may be: 示例端点可能是:

public async Task<IHttpActionResult> CreateStuffAsync()
{
    var query = this.queryService.RetrieveAsync();

    // logic to do stuff

    var update = this.updateService.Update(query);

    return this.Ok();
}

I don't create a single DbConnection and pass it around from the top, as this is a simplistic example, when in practise passing the connection between services would require a large refactor (although if necessary, this can be done). 我不创建单个DbConnection并从顶部传递它,因为这是一个简单的示例,当在实践中在服务之间传递连接时将需要大量的重构(尽管如有必要,也可以这样做)。 I also read that it is better to open/close the connections as necessary (ie keep them open for as little time as possible). 我还读到,最好根据需要打开/关闭连接(即,保持打开状态的时间尽可能短)。 The queryService and updateService open/close DbConnection 's via using statements: queryServiceupdateService通过using语句打开/关闭DbConnection

var factory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient");
using (var connection = factory.CreateConnection())
{
    connection.ConnectionString = "Data Source=localhost;Initial Catalog=MyDatabase;User ID=user;Password=password;Connect Timeout=300;AutoEnlist=true;";

    if (connection.State != ConnectionState.Open)
    {
        connection.Open();
    }

    var result = await connection.QueryAsync(Sql).ConfigureAwait(false);

    return result;
}

The same DbConnection is generally not used for multiple queries within the same API endpoint request -- but the same connection string is. 通常,相同的DbConnection不会用于同一API端点请求内的多个查询-而是使用相同的连接字符串。

Intermittently I am seeing an exception thrown when attempting to open the connection: 间歇性地打开尝试打开连接时看到异常:

"ExceptionType": "System.NotSupportedException",
"ExceptionMessage": "System.NotSupportedException: MySQL Connector/Net does not currently support distributed transactions.\r\n   at MySql.Data.MySqlClient.ExceptionInterceptor.Throw(Exception exception)\r\n   at MySql.Data.MySqlClient.MySqlConnection.EnlistTransaction(Transaction transaction)\r\n   at MySql.Data.MySqlClient.MySqlConnection.Open()"

I do not understand why it is attempting to escalate the transaction to a distributed transaction, when all of the connections are against the same database. 我不明白当所有连接都针对同一个数据库时,为什么它试图将事务升级为分布式事务。 Or am I misunderstanding/misusing the TransactionScope and DbConnection instances? 还是我误解/滥用了TransactionScopeDbConnection实例?

The System.Transactions.Transaction object makes the determination of whether to escalate to a distributed transaction based on how many separate "resource managers" (eg, a database) have enlisted in the transaction. System.Transactions.Transaction对象基于已在事务中登记了多少个单独的“资源管理器”(例如,数据库)来确定是否升级为分布式事务。

It does not draw a distinction between connections to different physical databases (that do require a distributed transaction) and multiple MySqlConnection connections that have the same connection string and connect to the same database (which might not). 它不会在到不同物理数据库的连接(确实需要分布式事务)与具有相同连接字符串并连接到同一数据库的多个MySqlConnection连接(可能没有)之间进行区分。 (It would be very difficult for it to determine that two separate "resource managers" ① represent the same physical DB and ② are being used sequentially, not in parallel.) As a result, when multiple MySqlConnection objects enlist in a transaction, it will always escalate to a distributed transaction. (很难确定两个单独的“资源管理器”①代表同一物理数据库,而②正在依次而不是并行使用)。结果,当多个MySqlConnection对象加入一个事务时,它将始终升级为分布式事务。

When this happens, you run into MySQL bug #70587 that distributed transactions aren't supported in Connector/NET. 发生这种情况时,您会遇到MySQL错误#70587 ,该错误表明Connector / NET中不支持分布式事务。

Workarounds would be: 解决方法是:

  1. Make sure only one MySqlConnection object is opened within any TransactionScope . 确保在任何TransactionScope仅打开一个MySqlConnection对象。
  2. Change to a separate connector that does support distributed transactions. 更改为不支持分布式事务的单独连接器。 You could use MySqlConnector ( NuGet , GitHub ) as a drop-in replacement for Connector/NET. 您可以使用MySqlConnector( NuGetGitHub )替代Connector / NET。 I've heard that dotConnect for MySQL supports them also (but haven't tried that one). 我听说MySQL的dotConnect也支持它们(但还没有尝试过)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM