Transações distribuídas entre bancos de dados na nuvem

Aplica-se a:Banco de Dados SQL do AzureInstância Gerenciada de SQL do Azure

Este artigo descreve o uso de transações de banco de dados elástico que permitem executar transações distribuídas em bancos de dados de nuvem no Banco de Dados SQL do Azure e na Instância Gerenciada de SQL do Azure. Neste artigo, os termos "transações distribuídas" e "transações de banco de dados elástico" são considerados sinônimos e são usados de maneira intercambiável.

Observação

Use também o Coordenador de Transações Distribuídas para a Instância Gerenciada de SQL do Azure para executar transações distribuídas em ambientes mistos.

Visão geral

As transações de banco de dados elástico para o banco de dados SQL do Azure e a Instância Gerenciada de SQL do Azure permitem que você execute transações que abrangem vários bancos de dados. As transações de banco de dados elástico estão disponíveis para aplicativos .NET usando ADO .NET e se integram à experiência de programação conhecida usando as classes System.Transaction. Para obter a biblioteca, confira .NET Framework 4.6.1 (Web Installer). Além disso, para a Instância Gerenciada, transações distribuídas estão disponíveis no Transact-SQL.

No local, um cenário como esse geralmente exige a execução do MSDTC (Coordenador de Transações Distribuídas da Microsoft). Como o MSDTC não está disponível para o Banco de Dados SQL do Azure, a capacidade de coordenar as transações distribuídas foi diretamente integrada à Instância Gerenciada de SQL ou ao Banco de Dados SQL. No entanto, para a Instância Gerenciada de SQL, você também pode usar o Coordenador de Transações Distribuídas para executar transações distribuídas em vários ambientes mistos, como entre instâncias gerenciadas, SQL Servers, outros RDBMSs (sistemas de gerenciamento de banco de dados relacionais), aplicativos personalizados e outros participantes de transação hospedados em qualquer ambiente que possa estabelecer conectividade de rede com o Azure.

Os aplicativos podem se conectar a qualquer banco de dados para iniciar transações distribuídas, e um dos bancos de dados ou servidores coordenará de modo transparente a transação distribuída, como mostra a figura a seguir.

Distributed transactions with Azure SQL Database using elastic database transactions

Cenários comuns

As transações de banco de dados elástico permitem que os aplicativos façam alterações atômicas nos dados armazenados em vários bancos de dados diferentes. Tanto o Banco de Dados SQL quanto a Instância Gerenciada de SQL oferecem suporte a experiências de desenvolvimento no lado do cliente em C# e .NET. A experiência do lado do servidor (código escrito em procedimentos armazenados ou scripts do lado do servidor) usando o Transact-SQL está disponível somente para a Instância Gerenciada de SQL.

Importante

Não há suporte para a execução de transações de banco de dado elástico entre o Banco de Dados SQL do Azure e a Instância Gerenciada de SQL do Azure. A transação de banco de dados elástico só pode abranger um conjunto de bancos de dados no Banco de Dados SQL ou um conjunto de bancos de dados entre instâncias gerenciadas.

As transações de banco de dados elástico têm como alvo os seguintes cenários:

  • Aplicativos de vários bancos de dados no Azure: nesse cenário, os dados são particionados verticalmente em vários bancos de dados na Instância Gerenciada de SQL ou no Banco de Dados SQL de maneira a que os diferentes tipos de dados residam em bancos de dados diferentes. Algumas operações exigem alterações nos dados, que são mantidos em dois ou mais bancos de dados. O aplicativo usa transações de banco de dados elástico para coordenar as alterações nos bancos de dados e garantir a atomicidade.
  • Aplicativos de banco de dados fragmentado no Azure: nesse cenário, a camada de dados usa a biblioteca de cliente do Banco de Dados Elástico ou a autofragmentação para particionar horizontalmente os dados em vários bancos de dados na Instância Gerenciada de SQL ou no Banco de Dados SQL. Um caso de uso importante é a necessidade de realizar mudanças atômicas em um aplicativo multilocatário fragmentado quando as alterações se estendem aos locatários. Pense, por exemplo, em uma transferência de um locatário para outro, ambos residentes em bancos de dados diferentes. Um segundo caso é a fragmentação refinada para acomodar as necessidades de capacidade de um locatário grande, o que, por sua vez, geralmente implica na necessidade de extensão de algumas operações atômicas em vários bancos de dados usados para o mesmo locatário. Um terceiro caso são as atualizações atômicas de dados de referência que são replicados nos bancos de dados. As operações atômicas e transacionadas nessa mesma linha agora podem ser coordenadas em vários bancos. As transações de banco de dados elástico usam o commit de duas fases para garantir a atomicidade das transações entre bancos de dados. É uma boa opção para as transações que envolvem menos de 100 bancos de dados por vez em uma só transação. Esses limites não são impostos, mas o desempenho e as taxas de êxito das transações de banco de dados elástico provavelmente serão prejudicados se eles forem ultrapassados.

Instalação e migração

As funcionalidades das transações de banco de dados elástico são fornecidas por meio de atualizações das bibliotecas .NET System.Data.dll e System.Transactions.dll. As DLLs garantem que o protocolo 2PC seja usado quando necessário para garantir a atomicidade. Para começar a desenvolver aplicativos usando as transações de banco de dados elástico, instale o .NET Framework 4.6.1 ou uma versão posterior. Quando executadas em uma versão anterior do .NET Framework, as transações não serão promovidas a uma transação distribuída, e uma exceção será gerada.

Após a instalação, você pode usar as APIs de transação distribuída em System.Transactions com conexões à Instância Gerenciada de SQL e ao Banco de Dados SQL. Se você já tiver aplicativos do MSDTC usando essas APIs, recompile os aplicativos existentes para o .NET 4.6 depois de instalar o .NET Framework 4.6.1. Se seus projetos tiverem como destino o .NET 4.6, eles usarão automaticamente as DLLs atualizadas da nova versão do Framework, e as chamadas à API de transação distribuída em conjunto com as conexões à Instância Gerenciada de SQL ou ao Banco de Dados SQL passarão a funcionar.

Lembre-se de que as transações de banco de dados elástico não requerem a instalação do MSDTC. Em vez disso, as transações de banco de dados elástico são gerenciadas diretamente pelo serviço, dentro dele. Isso simplifica significativamente os cenários de nuvem, já que a implantação de um MSDTC não é necessária para o uso das transações distribuídas com a Instância Gerenciada de SQL ou o Banco de Dados SQL. A Seção 4 explica detalhadamente como implantar as transações de banco de dados elástico e o .NET Framework necessário juntamente com seus aplicativos em nuvem no Azure.

Instalação do .NET para os Serviços de Nuvem do Azure

O Azure fornece várias ofertas para hospedar aplicativos .NET. Uma comparação entre as diferentes ofertas está disponível em Comparação entre o Serviço de Aplicativo do Azure, os Serviços de Nuvem e as Máquinas Virtuais. Se o SO convidado da oferta for inferior ao .NET 4.6.1 exigido para transações elásticas, será necessário atualizar o SO para 4.6.1.

Para Serviço de Aplicativo do Azure, não há suporte para atualizações para o SO convidado no momento. Para as Máquinas virtuais do Azure, basta fazer logon na VM e executar o instalador do .NET Framework mais recente. Para os Serviços de Nuvem do Azure, você precisará incluir a instalação de uma versão mais recente do .NET em tarefas de inicialização da sua implantação. Os conceitos e as etapas estão documentados em Instalar o .NET em uma Função do Serviço de Nuvem.

Observe que o instalador do .NET 4.6.1 pode exigir mais armazenamento temporário durante o processo de inicialização nos Serviços de Nuvem do Azure que o instalador do .NET 4.6. Para garantir uma instalação bem-sucedida, você precisa aumentar o armazenamento temporário para o serviço de nuvem do Azure no seu arquivo ServiceDefinition.csdef na seção LocalResources e nas configurações do ambiente de sua tarefa de inicialização, conforme mostrado no exemplo a seguir:

<LocalResources>
...
    <LocalStorage name="TEMP" sizeInMB="5000" cleanOnRoleRecycle="false" />
    <LocalStorage name="TMP" sizeInMB="5000" cleanOnRoleRecycle="false" />
</LocalResources>
<Startup>
    <Task commandLine="install.cmd" executionContext="elevated" taskType="simple">
        <Environment>
    ...
            <Variable name="TEMP">
                <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/LocalResources/LocalResource[@name='TEMP']/@path" />
            </Variable>
            <Variable name="TMP">
                <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/LocalResources/LocalResource[@name='TMP']/@path" />
            </Variable>
        </Environment>
    </Task>
</Startup>

Experiência de desenvolvimento do .NET

Aplicativos de vários bancos de dados

O exemplo de código a seguir usa a experiência de programação conhecida com o .NET System.Transactions. A classe TransactionScope estabelece uma transação de ambiente no .NET. (Uma "transação de ambiente" é aquela que reside no thread atual.) Todas as conexões abertas no TransactionScope participam da transação. Se diferentes bancos de dados participarem, a transação será elevada automaticamente a uma transação distribuída. O resultado da transação é controlado pela definição do escopo a concluir para indicar uma confirmação.

using (var scope = new TransactionScope())
{
    using (var conn1 = new SqlConnection(connStrDb1))
    {
        conn1.Open();
        SqlCommand cmd1 = conn1.CreateCommand();
        cmd1.CommandText = string.Format("insert into T1 values(1)");
        cmd1.ExecuteNonQuery();
    }
    using (var conn2 = new SqlConnection(connStrDb2))
    {
        conn2.Open();
        var cmd2 = conn2.CreateCommand();
        cmd2.CommandText = string.Format("insert into T2 values(2)");
        cmd2.ExecuteNonQuery();
    }
    scope.Complete();
}

Aplicativos de banco de dados fragmentado

As transações de banco de dados elástico da Instância Gerenciada e do Banco de Dados SQL também oferecem suporte a coordenação de transações distribuídas, em que você usa o método OpenConnectionForKey da biblioteca do cliente de banco de dados elástico para abrir conexões para uma camada de dados escalados horizontalmente. Considere os casos em que você precisa garantir a consistência transacional das alterações em vários valores-chave de fragmentação diferentes. As conexões com os fragmentos que hospedam os diferentes valores-chave de fragmentação são intermediadas usando o OpenConnectionForKey. Em geral, as conexões podem ocorrer para diferentes fragmentos, de modo que para assegurar as garantias transacionais seja necessária uma transação distribuída. O exemplo de código a seguir ilustra essa abordagem. Ele pressupõe que uma variável chamada shardmap seja usada para representar um mapa do fragmento na biblioteca do cliente de banco de dados elástico:

using (var scope = new TransactionScope())
{
    using (var conn1 = shardmap.OpenConnectionForKey(tenantId1, credentialsStr))
    {
        SqlCommand cmd1 = conn1.CreateCommand();
        cmd1.CommandText = string.Format("insert into T1 values(1)");
        cmd1.ExecuteNonQuery();
    }
    using (var conn2 = shardmap.OpenConnectionForKey(tenantId2, credentialsStr))
    {
        var cmd2 = conn2.CreateCommand();
        cmd2.CommandText = string.Format("insert into T1 values(2)");
        cmd2.ExecuteNonQuery();
    }
    scope.Complete();
}

Experiência de desenvolvimento do Transact-SQL

As transações distribuídas no lado do servidor usando o Transact-SQL estão disponíveis somente para a Instância Gerenciada de SQL do Azure. A transação distribuída pode ser executada somente entre as instâncias que pertencem ao mesmo grupo de relação de confiança do servidor. Nesse cenário, as instâncias gerenciadas precisam usar o servidor vinculado para se referenciarem.

O código Transact-SQL de exemplo a seguir usa BEGIN DISTRIBUTED TRANSACTION para iniciar a transação distribuída.

    -- Configure the Linked Server
    -- Add one Azure SQL Managed Instance as Linked Server
    EXEC sp_addlinkedserver
        @server='RemoteServer', -- Linked server name
        @srvproduct='',
        @provider='MSOLEDBSQL', -- Microsoft OLE DB Driver for SQL Server
        @datasrc='managed-instance-server.46e7afd5bc81.database.windows.net' -- SQL Managed Instance endpoint

    -- Add credentials and options to this Linked Server
    EXEC sp_addlinkedsrvlogin
        @rmtsrvname = 'RemoteServer', -- Linked server name
        @useself = 'false',
        @rmtuser = '<login_name>',         -- login
        @rmtpassword = '<secure_password>' -- password

    USE AdventureWorks2022;
    GO
    SET XACT_ABORT ON;
    GO
    BEGIN DISTRIBUTED TRANSACTION;
    -- Delete candidate from local instance.
    DELETE AdventureWorks2022.HumanResources.JobCandidate
        WHERE JobCandidateID = 13;
    -- Delete candidate from remote instance.
    DELETE RemoteServer.AdventureWorks2022.HumanResources.JobCandidate
        WHERE JobCandidateID = 13;
    COMMIT TRANSACTION;
    GO

Como combinar as experiências de desenvolvimento do .NET e do Transact-SQL

Aplicativos .NET que usam classes System.Transaction podem combinar a classe TransactionScope com a instrução Transact-SQL BEGIN DISTRIBUTED TRANSACTION. No TransactionScope, a transação interna que executa a transação BEGIN DITRIBUTED TRANSACTION será explicitamente promovida para a transação distribuída. Além disso, quando a segunda SqlConnection for aberta dentro do TransactionScope, será promovida implicitamente para a transação distribuída. Depois que a transação distribuída for iniciada, todas as solicitações de transações subsequentes, sejam provenientes do .NET ou do Transact-SQL, ingressarão na transação distribuída pai. Como consequência, todos os escopos de transação aninhados iniciados pela instrução BEGIN terminarão na mesma transação e as instruções COMMIT/ROLLBACK terão o seguinte efeito sobre o resultado geral:

  • A instrução COMMIT não terá nenhum efeito sobre o escopo da transação iniciado pela instrução BEGIN, ou seja, nenhum resultado será confirmado antes de o método Complete() ser invocado no objeto TransactionScope. Se o objeto TransactionScope for destruído antes de ser concluído, todas as alterações feitas no escopo serão revertidas.
  • A instrução ROLLBACK fará com que o TransactionScope inteiro seja revertido. Qualquer tentativa de inscrever novas transações dentro do TransactionScope falhará posteriormente, bem como tentar invocar Complete() no objeto TransactionScope.

Aqui está um exemplo em que a transação é promovida explicitamente para a transação distribuída com Transact-SQL.

using (TransactionScope s = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(DB0_ConnectionString)
    {
        conn.Open();
    
        // Transaction is here promoted to distributed by BEGIN statement
        //
        Helper.ExecuteNonQueryOnOpenConnection(conn, "BEGIN DISTRIBUTED TRAN");
        // ...
    }
 
    using (SqlConnection conn2 = new SqlConnection(DB1_ConnectionString)
    {
        conn2.Open();
        // ...
    }
    
    s.Complete();
}

O exemplo a seguir mostra uma transação que é promovida implicitamente para a transação distribuída depois que a segunda SqlConnection foi iniciada dentro do TransactionScope.

using (TransactionScope s = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(DB0_ConnectionString)
    {
        conn.Open();
        // ...
    }
    
    using (SqlConnection conn = new SqlConnection(DB1_ConnectionString)
    {
        // Because this is second SqlConnection within TransactionScope transaction is here implicitly promoted distributed.
        //
        conn.Open(); 
        Helper.ExecuteNonQueryOnOpenConnection(conn, "BEGIN DISTRIBUTED TRAN");
        Helper.ExecuteNonQueryOnOpenConnection(conn, lsQuery);
        // ...
    }
    
    s.Complete();
}

Transações para Banco de Dados SQL

Há suporte para transações de Banco de Dados Elástico entre diferentes servidores no Banco de Dados SQL do Azure. Quando as transações cruzam os limites do servidor, os servidores participantes precisam primeiro serem inseridos em uma relação de comunicação comum. Após a relação de comunicação ser estabelecida, qualquer banco de dados em qualquer um dos dois servidores poderá participar de transações elásticas com bancos de dados do outro servidor. Com transações abrangendo mais de dois servidores, uma relação de comunicação deve estar em vigor para qualquer par de servidores.

Use os cmdlets do PowerShell a seguir para gerenciar as relações de comunicação entre servidores para transações de Banco de Dados Elástico:

  • New-AzSqlServerCommunicationLink: use esse cmdlet para criar uma relação de comunicação entre dois servidores no Banco de Dados SQL do Azure. A relação é simétrica, o que significa que ambos os servidores podem iniciar transações com outro.
  • Get-AzSqlServerCommunicationLink: use esse cmdlet para recuperar as relações de comunicação existentes e suas propriedades.
  • Remove-AzSqlServerCommunicationLink: use esse cmdlet para remover as relações de comunicação existentes.

Transações para a Instância Gerenciada de SQL

As transações distribuídas têm suporte ao longo de bancos de dados em várias instâncias. Quando as transações cruzam os limites da instância gerenciada, as instâncias participantes precisam primeiro estar inseridas em uma relação de segurança e comunicação mútuas. Isso é feito criando-se um grupo de confiança do servidor, que pode ser feito usando o portal do Azure, o Azure PowerShell ou a CLI do Azure. Se as instâncias não estiverem na mesma rede virtual, o emparelhamento de rede virtual precisará ser configurado, e as regras de entrada e saída do grupo de segurança de rede precisarão permitir as portas 5024 e 11000-12000 em todas as redes virtuais participantes.

Server Trust Groups on Azure Portal

O diagrama a seguir mostra um grupo de confiança do servidor com instâncias gerenciadas que podem executar transações distribuídas com o .NET ou o Transact-SQL:

Distributed transactions with Azure SQL Managed Instance using elastic transactions

Monitorando o status da transação

Use as DMVs (Exibições de Gerenciamento Dinâmico) para monitorar o status e o progresso das transações de banco de dados elástico em andamento. Todas as DMVs relacionadas a transações são relevantes para as transações distribuídas na Instância Gerenciada de SQL e no Banco de Dados SQL. Você pode encontrar a lista correspondente de DMVs aqui: Funções e exibições de gerenciamento dinâmico relacionadas a transações (Transact-SQL).

Estas DMVs são especialmente úteis:

  • sys.dm_tran_active_transactions: lista as transações atualmente ativas e seu status. A coluna UOW (Unidade de Trabalho) pode identificar as diferentes transações filho que pertencem à mesma transação distribuída. Todas as transações dentro da mesma transação distribuída carregam o mesmo valor UOW. Para saber mais, confira a documentação do DMV.
  • sys.dm_tran_database_transactions: fornece informações adicionais sobre as transações, como a colocação da transação no log. Para saber mais, confira a documentação do DMV.
  • sys.dm_tran_locks: fornece informações sobre os bloqueios que são atualmente mantidos por transações em andamento. Para saber mais, confira a documentação do DMV.

Limitações

As seguintes limitações se aplicam atualmente para as transações de banco de dados elástico no Banco de Dados SQL:

  • Há suporte apenas para transações entre bancos de dados no Banco de Dados SQL. Outros provedores de recursos X/Open XA e bancos de dados fora do Banco de Dados SQL não podem participar de transações de banco de dados elástico. Isso significa que as transações do banco de dados elástico não podem se estender para o SQL Server local e o Banco de Dados SQL do Azure. Para transações distribuídas no local, continue a usar o MSDTC.
  • Há suporte somente para transações coordenadas pelo cliente a partir de um aplicativo .NET. Há planos para suporte do lado do servidor para o T-SQL, como INICIAR TRANSAÇÃO DISTRIBUÍDA, mas ainda não está disponível.
  • Não há suporte para transações em serviços WCF. Por exemplo, você tem um método de serviço WCF que executa uma transação. Colocar a chamada dentro de um escopo de transação falhará como System.ServiceModel.ProtocolException.

No momento, as seguintes limitações se aplicam a transações distribuídas (também conhecidas como transações elásticas ou transações distribuídas com suporte nativo) na Instância Gerenciada de SQL:

  • Com essa tecnologia, há suporte apenas para transações entre bancos de dados nas instâncias gerenciadas. Para todos os outros cenários que podem incluir provedores de recursos X/Open XA e bancos de dados fora da Instância Gerenciada de SQL do Azure, você deve configurar o DTC para Instância Gerenciada de SQL do Azure.
  • Não há suporte para transações em serviços WCF. Por exemplo, você tem um método de serviço WCF que executa uma transação. Colocar a chamada dentro de um escopo de transação falhará como System.ServiceModel.ProtocolException.
  • A Instância Gerenciada de SQL do Azure deve fazer parte de um grupo de confiança do servidor para participar da transação distribuída.
  • As limitações dos grupos de confiança do servidor afetam as transações distribuídas.
  • As Instâncias Gerenciadas que participam de transações distribuídas precisam ter conectividade sobre pontos de extremidade privados (usando o endereço IP privado da rede virtual em que são implantadas) e precisam ser referenciadas mutuamente usando FQDNs privados. Os aplicativos cliente podem usar transações distribuídas em pontos de extremidade privados. Além disso, em casos em que o Transact-SQL aproveita os servidores vinculados que fazem referência a pontos de extremidade privados, os aplicativos cliente também podem usar transações distribuídas em pontos de extremidade públicos. Essa limitação é explicada no diagrama a seguir.

Private endpoint connectivity limitation

Próximas etapas