Pool de conexões do SQL Server (ADO.NET)

A conexão com um servidor de banco de dados normalmente consiste em várias etapas demoradas. Um canal físico, como um soquete ou um pipe nomeado, deve ser estabelecido, o handshake inicial com o servidor deve ocorrer, as informações da cadeia de conexão devem ser analisadas, a conexão deve ser autenticada pelo servidor, as verificações devem ser executadas para o alistamento na transação atual e assim por diante.

Na prática, a maioria dos aplicativos usa apenas uma ou algumas configurações diferentes para conexões. Isso significa que, durante a execução do aplicativo, muitas conexões idênticas serão repetidamente abertas e fechadas. Para minimizar o custo de abertura de conexões, o ADO.NET usa uma técnica de otimização chamada pool de conexões.

O pool de conexões reduz o número de vezes que novas conexões devem ser abertas. O pooler mantém a propriedade da conexão física. Ele gerencia conexões mantendo vivo um conjunto de conexões ativas para cada configuração de conexão dada. Sempre que um usuário chama Open uma conexão, o pooler procura uma conexão disponível no pool. Se uma conexão em pool estiver disponível, ela a retornará ao chamador em vez de abrir uma nova conexão. Quando o aplicativo chama Close a conexão, o pooler a retorna para o conjunto agrupado de conexões ativas em vez de fechá-la. Assim que a conexão for retornada ao pool, ela estará pronta para ser reutilizada na próxima Open chamada.

Somente conexões com a mesma configuração podem ser agrupadas. ADO.NET mantém vários pools ao mesmo tempo, um para cada configuração. As conexões são separadas em pools por cadeia de conexão e por identidade do Windows quando a segurança integrada é usada. As conexões também são agrupadas com base no fato de estarem alistadas em uma transação. Ao usar ChangePasswordo , a SqlCredential instância afeta o pool de conexões. Instâncias diferentes de usarão pools de conexões diferentes, mesmo que o ID de SqlCredential usuário e a senha sejam os mesmos.

O agrupamento de conexões pode melhorar significativamente o desempenho e a escalabilidade do seu aplicativo. Por padrão, o pool de conexões é habilitado no ADO.NET. A menos que você o desative explicitamente, o pooler otimiza as conexões à medida que elas são abertas e fechadas em seu aplicativo. Você também pode fornecer vários modificadores de cadeia de conexão para controlar o comportamento do pool de conexões. Para obter mais informações, consulte "Controlando o pool de conexões com palavras-chave de cadeia de conexão" mais adiante neste tópico.

Nota

Quando o pool de conexões estiver habilitado, e se ocorrer um erro de tempo limite ou outro erro de login, uma exceção será lançada e as tentativas de conexão subsequentes falharão pelos próximos cinco segundos, o "período de bloqueio". Se o aplicativo tentar se conectar dentro do período de bloqueio, a primeira exceção será lançada novamente. Falhas subsequentes após o término de um período de bloqueio resultarão em novos períodos de bloqueio duas vezes mais longos do que o período de bloqueio anterior, até um máximo de um minuto.

Criação e atribuição de pool

Quando uma conexão é aberta pela primeira vez, um pool de conexões é criado com base em um algoritmo de correspondência exata que associa o pool à cadeia de conexão na conexão. Cada pool de conexões está associado a uma cadeia de conexão distinta. Quando uma nova conexão é aberta, se a cadeia de conexão não for uma correspondência exata com um pool existente, um novo pool será criado. As conexões são agrupadas por processo, por domínio de aplicativo, por cadeia de conexão e, quando a segurança integrada é usada, por identidade do Windows. As cadeias de conexão também devem ser uma correspondência exata; As palavras-chave fornecidas em uma ordem diferente para a mesma conexão serão agrupadas separadamente.

No exemplo C# a seguir, três novos SqlConnection objetos são criados, mas apenas dois pools de conexões são necessários para gerenciá-los. Observe que a primeira e a segunda cadeias de conexão diferem pelo valor atribuído a Initial Catalog.

using (SqlConnection connection = new SqlConnection(  
  "Integrated Security=SSPI;Initial Catalog=Northwind"))  
    {  
        connection.Open();
        // Pool A is created.  
    }  
  
using (SqlConnection connection = new SqlConnection(  
  "Integrated Security=SSPI;Initial Catalog=pubs"))  
    {  
        connection.Open();
        // Pool B is created because the connection strings differ.  
    }  
  
using (SqlConnection connection = new SqlConnection(  
  "Integrated Security=SSPI;Initial Catalog=Northwind"))  
    {  
        connection.Open();
        // The connection string matches pool A.  
    }  

Se Min Pool Size não for especificado na cadeia de conexão ou for especificado como zero, as conexões no pool serão fechadas após um período de inatividade. No entanto, se o especificado Min Pool Size for maior que zero, o pool de conexões não será destruído até que o AppDomain seja descarregado e o processo termine. A manutenção de pools inativos ou vazios envolve uma sobrecarga mínima do sistema.

Nota

O pool é automaticamente limpo quando ocorre um erro fatal, como um failover.

Adicionando conexões

Um pool de conexões é criado para cada cadeia de conexão exclusiva. Quando um pool é criado, vários objetos de conexão são criados e adicionados ao pool para que o requisito de tamanho mínimo do pool seja atendido. As conexões são adicionadas ao pool conforme necessário, até o tamanho máximo especificado (100 é o padrão). As conexões são liberadas de volta para o pool quando são fechadas ou descartadas.

Quando um SqlConnection objeto é solicitado, ele é obtido do pool se uma conexão utilizável estiver disponível. Para ser utilizável, uma conexão deve não ser usada, ter um contexto de transação correspondente ou não estar associada a qualquer contexto de transação e ter um link válido para o servidor.

O pool de conexões satisfaz as solicitações de conexões realocando conexões à medida que elas são liberadas de volta para o pool. Se o tamanho máximo do pool tiver sido atingido e nenhuma conexão utilizável estiver disponível, a solicitação será enfileirada. Em seguida, o pooler tenta recuperar quaisquer conexões até que o tempo limite seja atingido (o padrão é 15 segundos). Se o pooler não puder atender à solicitação antes que a conexão atinja o tempo limite, uma exceção será lançada.

Atenção

É altamente recomendável que você sempre feche a conexão quando terminar de usá-la para que a conexão seja retornada ao pool. Você pode fazer isso usando os Close métodos ou Dispose do objeto, ou abrindo todas as conexões dentro de Connection uma using instrução em C# ou uma Using instrução no Visual Basic. As conexões que não são fechadas explicitamente podem não ser adicionadas ou retornadas ao pool. Para obter mais informações, consulte usando instrução ou Como: Eliminar um recurso do sistema para Visual Basic.

Nota

Não chame Close ou Dispose em um Connection, um DataReader, ou qualquer outro objeto gerenciado no Finalize método de sua classe. Em um finalizador, libere apenas recursos não gerenciados que sua classe possui diretamente. Se sua classe não possui recursos não gerenciados, não inclua um Finalize método em sua definição de classe. Para obter mais informações, consulte Coleta de lixo.

Para obter mais informações sobre os eventos associados à abertura e ao fechamento de conexões, consulte Classe de evento de logon de auditoria e Classe de evento de logout de auditoria na documentação do SQL Server.

Removendo conexões

O pool de conexões remove uma conexão do pool depois que ela estiver ociosa por aproximadamente 4 a 8 minutos, ou se o pooler detetar que a conexão com o servidor foi cortada. Observe que uma conexão cortada pode ser detetada somente depois de tentar se comunicar com o servidor. Se for encontrada uma conexão que não está mais conectada ao servidor, ela será marcada como inválida. As conexões inválidas são removidas do pool de conexões somente quando são fechadas ou recuperadas.

Se existir uma conexão com um servidor que desapareceu, essa conexão pode ser extraída do pool mesmo que o pool de conexões não tenha detetado a conexão cortada e a marcado como inválida. Isso ocorre porque a sobrecarga de verificar se a conexão ainda é válida eliminaria os benefícios de ter um pooler, fazendo com que outra viagem de ida e volta ao servidor ocorresse. Quando isso ocorre, a primeira tentativa de usar a conexão detetará que a conexão foi cortada e uma exceção será lançada.

Limpar a piscina

ADO.NET 2.0 introduziu dois novos métodos para limpar o pool: ClearAllPools e ClearPool. ClearAllPools Limpa os pools de conexões de um determinado provedor e ClearPool limpa o pool de conexões associado a uma conexão específica. Se houver conexões sendo usadas no momento da chamada, elas serão marcadas adequadamente. Quando estão fechados, são descartados em vez de serem devolvidos à piscina.

Suporte a transações

As conexões são extraídas do pool e atribuídas com base no contexto da transação. A menos que Enlist=false seja especificado na cadeia de conexão, o pool de conexões garante que a conexão esteja alistada no Current contexto. Quando uma conexão é fechada e retornada ao pool com uma transação alistada System.Transactions , ela é deixada de lado de tal forma que a próxima solicitação para esse pool de conexões com a mesma System.Transactions transação retornará a mesma conexão, se estiver disponível. Se tal solicitação for emitida e não houver conexões agrupadas disponíveis, uma conexão será extraída da parte não transacionada do pool e alistada. Se nenhuma conexão estiver disponível em qualquer área do pool, uma nova conexão será criada e alistada.

Quando uma conexão é fechada, ela é liberada de volta para o pool e para a subdivisão apropriada com base em seu contexto de transação. Portanto, você pode fechar a conexão sem gerar um erro, mesmo que uma transação distribuída ainda esteja pendente. Isso permite que você confirme ou anule a transação distribuída mais tarde.

Controlando o pool de conexões com palavras-chave de cadeia de conexão

A ConnectionString propriedade do objeto suporta pares chave/valor da cadeia de SqlConnection conexão que podem ser usados para ajustar o comportamento da lógica do pool de conexões. Para obter mais informações, veja ConnectionString.

Fragmentação da piscina

A fragmentação do pool é um problema comum em muitos aplicativos Web em que o aplicativo pode criar um grande número de pools que não são liberados até que o processo seja encerrado. Isso deixa um grande número de conexões abertas e consumindo memória, o que resulta em baixo desempenho.

Fragmentação do pool devido à segurança integrada

As conexões são agrupadas de acordo com a cadeia de conexão mais a identidade do usuário. Portanto, se você usar a autenticação Básica ou a Autenticação do Windows no site e um logon de segurança integrado, obterá um pool por usuário. Embora isso melhore o desempenho de solicitações de banco de dados subsequentes para um único usuário, esse usuário não pode aproveitar as conexões feitas por outros usuários. Isso também resulta em pelo menos uma conexão por usuário com o servidor de banco de dados. Esse é um efeito colateral de uma arquitetura de aplicativo Web específica que os desenvolvedores devem pesar em relação aos requisitos de segurança e auditoria.

Fragmentação do pool devido a muitos bancos de dados

Muitos provedores de serviços de Internet hospedam vários sites em um único servidor. Eles podem usar um único banco de dados para confirmar um logon de autenticação de formulários e, em seguida, abrir uma conexão com um banco de dados específico para esse usuário ou grupo de usuários. A conexão com o banco de dados de autenticação é agrupada e usada por todos. No entanto, há um pool separado de conexões para cada banco de dados, que aumentam o número de conexões com o servidor.

Este também é um efeito colateral do design do aplicativo. Há uma maneira relativamente simples de evitar esse efeito colateral sem comprometer a segurança quando você se conecta ao SQL Server. Em vez de se conectar a um banco de dados separado para cada usuário ou grupo, conecte-se ao mesmo banco de dados no servidor e execute a instrução Transact-SQL USE para alterar para o banco de dados desejado. O fragmento de código a seguir demonstra a criação de uma conexão inicial com o banco de dados e, master em seguida, alternar para o banco de dados desejado especificado na databaseName variável de cadeia de caracteres.

' Assumes that command is a valid SqlCommand object and that  
' connectionString connects to master.  
    command.Text = "USE DatabaseName"  
Using connection As New SqlConnection(connectionString)  
    connection.Open()  
    command.ExecuteNonQuery()  
End Using  
// Assumes that command is a SqlCommand object and that  
// connectionString connects to master.  
command.Text = "USE DatabaseName";  
using (SqlConnection connection = new SqlConnection(  
  connectionString))  
  {  
    connection.Open();  
    command.ExecuteNonQuery();  
  }  

Funções do aplicativo e pool de conexões

Depois que uma função de aplicativo do SQL Server tiver sido ativada chamando o sp_setapprole procedimento armazenado do sistema, o contexto de segurança dessa conexão não poderá ser redefinido. No entanto, se o pool estiver habilitado, a conexão será retornada ao pool e ocorrerá um erro quando a conexão em pool for reutilizada. Para obter mais informações, consulte o artigo da Base de Dados de Conhecimento, "SQL application role errors with OLE DB resource pooling".

Alternativas de função de aplicativo

Recomendamos que você aproveite os mecanismos de segurança que podem ser usados em vez de funções de aplicativo. Para obter mais informações, consulte Criando funções de aplicativo no SQL Server.

Consulte também