Introdução
A maior parte das aplicações desenvolidas
normalmente acessam banco de dados. Sempre que precisar acessar os dados no servidor,
SQL Server por exemplo, a aplicação o realizará através de um objeto de conexão
de um dos Data Providers do ADO.NET, por exemplo a classe
System.Data.SqlClient.SqlConnection. Para criar esta conexão é preciso informar
a string de conexão, que indica qual servidor e qual banco de dados será estabelecida
a conexão etc. Em resumo, a string
de conexão identifica o fornecedor do banco de dados, nome da máquina onde a aplicação
de banco de dados está instalada, nome do banco de dados da aplicação, informações
de segurança (usuário, senha, autenticação integrada etc) e parâmetros de controle
do Pool de Conexões, entre outras.
Imagine que sua aplicação tem uma
tela que mostra alguns registros resultantes de uma consulta. Já percebeu que existe
uma demora até que os dados sejam exibidos? Boa parte desta demora é porque sua
aplicação está criando uma conexão com banco de dados. Só que este processo de conexão
não é tão simples e tem um custo alto. Por que? Porque uma conexão com banco de
dados consiste de algumas tarefas que consomem recursos: uma comunicação física,
por exemplo um Socket, precisa ser estabelecida e para isso é necessário um handshake
inicial entre as máquinas; a string de conexão precisa ser analisada e validada;
a conexão deverá ser autenticada contra o servidor; algumas verificações para registro
numa transação corrente etc. Como uma aplicação precisa interagir várias vezes com
o banco de dados, dependendo de como esteja sendo utilizada, o desempenho e a escalabilidade
da aplicação podem ser comprometidos. Como essas conexões são custosas, percebe-se
a necessidade de reaproveitar as conexões que já foram estabelecidas e utilizá-las
da melhor forma possível.
A maioria das aplicações usam uma
ou poucas configurações de banco de dados diferentes, pois durante a execução da
aplicação várias conexões idênticas serão abertas e fechadas, o que poderá impactar
a performance da sua aplicação. Como resolver isso? Reutilização das conexões, ok?
Até porque você não precisa delas o tempo todo, e sim somente quando for necessário
executar algum comando contra o banco de dados.
Para minimizar o custo da abertura
das conexões, o ADO .NET dispõe de um recurso poderoso chamado Pool de Conexões
(ou Connection Pooling), que já está
habilitado por padrão no Data Provider para SQL Server, e ainda melhor no ADO.NET
2.0.
Este artigo tem por objetivo explicar
quase tudo que você precisa saber sobre o Pool de Conexões, seu funcionamento para
que você o utiize da melhor forma possível de forma eficiente para garantir o desempenho
e escalabilidade da sua aplicação.
O que é o Pool de Conexões?
O Pool de Conexões
é um repositório que mantém uma lista de conexões abertas e reutilizáveis. Uma aplicação
poderá conter um ou vários pools ao mesmo tempo e a qualquer momento, mas estes
não são compartilhados entre aplicações. Para cada string de conexão única será
criado um pool na primeira vez que a aplicação solicitar uma conexão para uma determinada
string de conexão. Se a aplicação fizer
uma nova requisição com uma string de conexão diferente, será criado um novo pool.
Todo o controle é garantido pelo Connection Pool Manager do ADO.NET, ou gerenciador
do pool. Com o Pool de Conexões não haverá mais necessidade de executar todo o procedimento
custoso sempre que a aplicação solicitar uma conexão e sua aplicação poderá reaproveitar
as conexões que já foram estabelecidas anteriormente. A
tabela 1 ilustra quatro strings de conexão, onde serão criados três pools
diferentes, um para cada string que não se repete.
Tabela 1: Exemplos de strings de conexão.
|
String de conexão
A:
|
|
string strConn =
"Server=tech-renatog;Initial Catalog=framework;User
ID=nomeusuario;Password=senhaforte";
using (SqlConnection
connection = new SqlConnection(strConn))
{
//A conexão deverá ser aberta somente
quando necessário
connection.Open();
//executa comandos etc
}//método Dispose() será invocado automaticamente
|
|
String de Conexão
B:
|
|
string strConn =
"Server=tech-renatog;Initial Catalog=framework;
ID=teste;Password=teste";
using (SqlConnection
connection = new SqlConnection(strConn))
{
//A conexão deverá ser aberta somente
quando necessário
connection.Open();
//executa comandos etc
}//método Dispose() será invocado automaticamente
|
|
String de Conexão
C:
|
|
string strConn =
"Server=tech-renatog;Initial Catalog=crm;Integrated
Security=SSPI";
using (SqlConnection
connection = new SqlConnection(strConn))
{
//A conexão deverá ser aberta somente
quando necessário
connection.Open();
//executa comandos etc
}
|
|
String de Conexão D:
|
|
string strConn =
"Server=tech-renatog;Initial Catalog=crm;Integrated
Security=SSPI";
using (SqlConnection
connection = new SqlConnection(strConn))
{
//A conexão deverá ser aberta somente
quando necessário
connection.Open();
//executa comandos etc
}
|
Quando uma conexão é aberta pela
primeira vez, neste momento é criado um pool de conexão baseado no algoritmo que
analisa a string de conexão e a associa ao pool criado recentemente. Cada pool é
associado a uma string de conexão única. Quando uma conexão é aberta, se a string
de conexão não combinar com alguma já existente no Pool de Conexões, um novo pool
será criado. O pool será mantido até que a aplicação seja encerrada e, mesmo que
o pool fique inativo ou vazio, não haverá um custo alto para a aplicação.
No exemplo da
tabela 1 temos quatro strings de conexão.
Para as strings A e B serão criados dois pools diferentes. Perceba que a
principal diferença entre as strings A e B é o contexto de segurança, pois conectam
o banco de dados com usuários distintos. Para as strings C e D será criado um pool
que será fragmentado devido a autenticação integrada. A conexão com autenticação
integrada provoca a fragmentação do pool porque as conexões são colocadas no pool
de acordo com a string de conexão mais a identidade do usuário, gerando um pool
por usuário. Embora melhore o desempenho da aplicação por usuário, um mesmo usário
não poderá se beneficiar das conexões feitas por outro usuário.
As conexões são adicionadas
ao pool por processo, por domínio de aplicação (App Domain), por string de conexão,
e quando usa segurança integrada, pelo Registro do Windows. É uma tecnologia que
atua do lado cliente (client-side), e o banco de dados não faz idéia se existe um
ou mais pools associado a uma aplicação. Tecnologia client-side porque o pool é
criado na máquina que está iniciando a conexão através de um objeto DbConnection.