Partilhar via


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

Aplica-se a: .NET Framework .NET .NET Standard

Baixar ADO.NET

A ligação a um servidor de base de dados normalmente consiste em vários passos demorados. Deve ser estabelecido um canal físico, como um socket ou um pipe nomeado, deve ocorrer o handshake inicial com o servidor, a informação da cadeia de ligação deve ser analisada, a ligação deve ser autenticada pelo servidor, devem ser feitas verificações para a entrada na transação atual, e assim sucessivamente.

Na prática, a maioria das aplicações utiliza apenas uma ou algumas configurações diferentes para as ligações. Isto significa que, durante a execução da aplicação, muitas ligações idênticas serão abertas e fechadas repetidamente. Para minimizar o custo de abertura de ligações, o Microsoft SqlClient Data Provider para SQL Server utiliza 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 ligação física. Gere as ligações mantendo ativo um conjunto de ligações ativas para cada configuração de ligação. Sempre que um utilizador liga Open a uma ligação, o pooler procura uma ligação disponível no pool. Se houver uma ligação em pool disponível, devolve-a ao chamador em vez de abrir uma nova ligação. Quando a aplicação liga Close à ligação, o pooler devolve-a ao conjunto agrupado de ligações ativas em vez de o fechar. Assim que a ligação é devolvida ao pool, estará pronta para ser reutilizada na próxima chamada Open.

Apenas ligações com a mesma configuração podem ser agrupadas. O Microsoft SqlClient Data Provider para SQL Server mantém vários pools ao mesmo tempo, um para cada configuração. As ligações são separadas em pools por cadeia de ligação e pela identidade do Windows quando é usada segurança integrada. As ligações também são agrupadas com base em se estão ou não envolvidas numa transação. Ao usar ChangePassword, a SqlCredential instância afeta o pool de ligações. Diferentes instâncias de SqlCredential vão usar diferentes pools de ligações, mesmo que o ID de utilizador e a palavra-passe sejam os mesmos.

Agrupar ligações pode melhorar significativamente o desempenho e a escalabilidade da sua aplicação. Por padrão, o pooling de ligações está ativado no Microsoft SqlClient Data Provider for SQL Server. A menos que a desatives explicitamente, o pooler otimiza as ligações à medida que são abertas e fechadas na tua aplicação. Também podes fornecer vários modificadores de string de ligação para controlar o comportamento do pool de ligações. Para mais informações, consulte "Controlar o Pool de Conexões com Palavras-Chave de String de Conexão" mais adiante neste tópico.

Importante

Quando o pooling de conexões está ativado e ocorre um erro de timeout ou outro erro de login, uma exceção será gerada e as tentativas subsequentes de ligação falharão durante os próximos 5 segundos, o "blocking period". Se a aplicação tentar ligar-se 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 resultam num novo período de bloqueio que é o dobro do período de bloqueio anterior, até um máximo de 1 minuto.

Observação

O mecanismo "blocking period" não se aplica ao Azure SQL Server por defeito. Este comportamento pode ser alterado modificando a PoolBlockingPeriod propriedade em ConnectionString exceto no .NET Standard.

Criação e atribuição de grupos

Quando uma ligação é aberta pela primeira vez, é criado um pool de ligações com base num algoritmo de correspondência exata que associa o pool à string de ligação na ligação. Cada pool de ligação está associado a uma cadeia de ligação distinta. Quando uma nova ligação é aberta, se a cadeia de ligação não for uma correspondência exata a um pool existente, é criado um novo pool.

Observação

As ligações são agrupadas por processo, por domínio de aplicação, por cadeia de ligação e, quando é usada segurança integrada, por identidade do Windows. As cadeias de ligação também devem ser uma correspondência exata; as palavras-chave fornecidas numa ordem diferente para a mesma ligação serão agrupadas separadamente.

Observação

Se MinPoolSize não for especificado na cadeia de ligação ou for especificado como zero, as ligações no pool serão encerradas após um período de inatividade. No entanto, se o especificado MinPoolSize for maior que zero, o pool de ligação não é destruído até que o AppDomain seja descarregado e o processo termine. A manutenção de piscinas inativas ou vazias implica uma sobrecarga mínima do sistema.

Observação

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

No exemplo seguinte de C#, são criados três novos SqlConnection objetos, mas apenas dois pools de ligação são necessários para os gerir. Note que a primeira e a segunda cadeias de ligaçã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.  
        }

Adicionar ligações

É criado um pool de ligações para cada cadeia de ligação única. Quando um pool é criado, múltiplos objetos de ligação são criados e adicionados ao pool para que o requisito mínimo de tamanho do pool seja satisfeito. As ligações são adicionadas ao pool conforme necessário, até ao tamanho máximo especificado (100 é o padrão). As ligações são libertadas de volta para a piscina quando são fechadas ou descartadas.

Quando um SqlConnection objeto é solicitado, é obtido do pool se houver uma ligação utilizável disponível. Para ser utilizável, uma ligação deve não ser utilizada, ter um contexto de transação correspondente ou não estar associada a qualquer contexto de transação, e ter uma ligação válida ao servidor.

O pooler de ligações satisfaz pedidos de ligações realocando-as conforme são devolvidas ao pool. Se o tamanho máximo do pool for atingido e não houver ligação utilizável disponível, o pedido é colocado em fila. O pooler tenta então recuperar todas as ligações até atingir o time-out (o padrão é de 15 segundos). Se o pooler não conseguir satisfazer o pedido antes de a ligação expirar, será lançada uma exceção.

Atenção

Recomendamos vivamente que feche sempre a ligação quando terminar de a usar, para que a ligação volte à piscina. Podes fazer isto usando ou os métodos Close ou Dispose do objeto Connection, ou então abrindo todas as conexões dentro de uma instrução using em C#, ou uma instrução Using no Visual Basic. As conexões que não são fechadas explicitamente podem não ser adicionadas ou retornadas ao pool. Para mais informações, consulte usar Instrução ou Como Eliminar: Descartar um Recurso-Sistema para Visual Basic.

Observação

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

Para mais informações sobre os eventos associados à abertura e encerramento de ligações, consulte Audit Login Event Class e Audit Logout Event Class na documentação do SQL Server.

Remover ligações

Se o LoadBalanceTimeout (ou Connection Lifetime) for definido, quando uma ligação é devolvida ao pool, o seu tempo de criação é comparado com o tempo atual e a ligação é destruída se esse intervalo de tempo (em segundos) exceder o valor especificado por LoadBalanceTimeout. Isto é útil em configurações agrupadas para forçar o balanceamento de carga entre um servidor em funcionamento e um servidor acabado de ser ativado.

Se o LoadBalanceTimeout (ou Tempo de Vida da Ligação) não estiver definido (valor padrão = 0), o pooler de conexões remove uma conexão do pool depois de esta estar inativa durante aproximadamente 4-8 minutos (de forma aleatória em duas passagens), ou se o pooler detetar que a conexão com o servidor foi cortada.

Observação

Uma ligação cortada só pode ser detetada após tentar comunicar com o servidor. Se for encontrada uma ligação que já não está ligada ao servidor, é marcada como inválida. As conexões inválidas são removidas do pool de conexões apenas quando são encerradas ou recuperadas.

Se existir uma ligação a um servidor que desapareceu, essa ligação pode ser extraída do pool mesmo que o gestor de ligação não tenha detetado a ligação cortada e a tenha marcado como inválida. Isto acontece porque a sobrecarga de verificar se a ligação ainda é válida eliminaria os benefícios de ter um pooler, ao causar outra viagem de ida e volta ao servidor. Quando isto acontece, a primeira tentativa de usar a ligação irá detetar que a ligação foi interrompida, e uma exceção é lançada.

Limpar a piscina

O Microsoft SqlClient Data Provider para SQL Server introduziu dois novos métodos para limpar o pool: ClearAllPools e ClearPool. ClearAllPools limpa os pools de ligação de um determinado fornecedor e ClearPool limpa o pool de ligação associado a uma ligação específica.

Observação

Se houver ligações a estarem a ser usadas no instante da chamada, elas são devidamente marcadas. Quando são fechados, são descartados em vez de serem devolvidos à piscina.

Suporte a transações

As ligaçõ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 ligação, o pool de conexões assegura que a ligação esteja alocada no contexto Current. Quando uma ligação é encerrada e devolvida ao pool com uma transação alistada System.Transactions, é configurada de tal forma que a próxima solicitação para esse pool de conexões com a mesma transação System.Transactions devolva a mesma ligação, se estiver disponível. Se for feito tal pedido e não houver ligações disponíveis no pool, uma ligação é extraída da parte não transacionada do pool e registada. Se não houver ligações disponíveis em qualquer área da piscina, é criada e recrutada uma nova ligação.

Quando uma ligação é encerrada, é retornada para o pool e para a subdivisão apropriada, com base no contexto de transação. Assim, pode fechar a ligação sem gerar erro, mesmo que uma transação distribuída ainda esteja pendente. Isto permite-lhe mais tarde comprometer ou abortar a transação distribuída.

Controla o agrupamento de conexões com parâmetros da cadeia de conexão

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

Fragmentação de pools

A fragmentação de pools é um problema comum em muitas aplicações Web, onde a aplicação pode criar um grande número de pools que só são libertados quando o processo termina. Isto deixa um grande número de ligações abertas e a consumir memória, o que resulta em desempenho fraco.

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

As ligações são agrupadas de acordo com a cadeia de ligação mais a identidade do utilizador. Portanto, se usar autenticação básica ou autenticação Windows no site e um login de segurança integrado, obtém um pool por utilizador. Embora isto melhore o desempenho dos pedidos subsequentes de base de dados para um único utilizador, esse utilizador não pode tirar partido das ligações feitas por outros utilizadores. Isto também resulta em pelo menos uma ligação por utilizador ao servidor da base de dados. Isto é um efeito secundário de uma arquitetura de aplicação Web específica que os programadores têm de ponderar face aos requisitos de segurança e auditoria.

Fragmentação de pools devido a muitas bases de dados

Muitos fornecedores de serviços de Internet alojam vários sites num único servidor. Podem usar uma única base de dados para confirmar um login de autenticação Forms e depois abrir uma ligação a uma base de dados específica para esse utilizador ou grupo de utilizadores. A ligação à base de dados de autenticação é agrupada e usada por todos. No entanto, existe um conjunto separado de ligações a cada base de dados, que aumenta o número de ligações ao servidor.

Isto é também um efeito secundário do design da aplicação. Existe uma forma relativamente simples de evitar este efeito secundário sem comprometer a segurança quando se liga ao SQL Server. Em vez de se ligar a uma base de dados separada para cada utilizador ou grupo, ligue-se à mesma base de dados no servidor e depois execute a instrução Transact-SQL USE para alterar para a base de dados desejada.

O fragmento de código seguinte demonstra a criação de uma ligação inicial à master base de dados e depois a mudança para a base de dados desejada especificada na databaseName variável string.

// Assume that connectionString connects to master.  
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand())
{
    connection.Open();
    command.Connection = connection;
    command.CommandText = "USE DatabaseName";
    command.ExecuteNonQuery();
}

Funções de aplicação e gestão de ligações

Depois de uma função de aplicação do SQL Server ter sido ativada ao chamar o sp_setapprole procedimento armazenado do sistema, o contexto de segurança dessa ligação não pode ser redefinido. No entanto, se o pooling estiver ativado, a ligação é devolvida ao pool, e um erro ocorre quando a conexão do pool é reutilizada.

Alternativas de funções de aplicação

Recomendamos que aproveite mecanismos de segurança que pode usar em vez de funções de aplicação.

Consulte também