Criando conexões com o banco de dados - Faça isso uma vez ou para cada consulta?

101

No momento, crio uma conexão com o banco de dados quando minha página da web é carregada pela primeira vez. Em seguida, processo a página e executo quaisquer consultas nessa conexão. Essa é a melhor maneira de fazer isso ou devo criar uma conexão com o banco de dados toda vez que executo uma consulta?

ps Faz mais sentido para mim criar uma conexão e usá-la, mas não sei se isso pode causar outros problemas.

Estou usando C # (ASP.NET) com MSSQL.

webnoob
fonte

Respostas:

124

Se você criar uma por consulta / transação, é muito mais fácil gerenciar o "fechamento" das conexões.

Percebo por que o senso comum determina que você deva abrir um e usá-lo por toda parte, mas você terá problemas com conexões interrompidas e multithreading. Portanto, seu próximo passo será abrir uma piscina, digamos 50, conexões e mantê-las todas abertas, distribuindo-as para diferentes processos. E então você descobrirá que isso é exatamente o que a estrutura .NET já faz por você .

Se você abrir uma conexão quando precisar e descartá-la quando terminar, que na verdade não a fechará, ela será devolvida ao pool de conexões para ser usada novamente.

pdr
fonte
Estava lendo esse artigo quando você o postou :) Obrigado.
Webnoob 29/03/12
2
A página da web à qual você vinculou é específica para o SQL Server. O .NET também fornece pool automático ao conectar-se a outros bancos de dados, por exemplo - Oracle, Sqlite, MySql?
Briddums 29/03/12
@ Briddums - Eu acho que depende do conector. .Net, por exemplo, não fornece um conector MySql. É escrito e mantido pelo MySql. E, embora funcione, na minha experiência, a implementação anterior estava longe de estar livre de erros.
ZweiBlumen
1
@ briddums: Depende da montagem do provedor. Estou certo de que a implementação Oracle da Microsoft e a própria Oracle oferecem suporte ao pool de conexões, porque eu as usei. Ouvi dizer que existe um MySQL, e espero que os provedores do Spring.NET dêem suporte ao pool, mas é melhor procurar ou perguntar diretamente ao provedor do que me perguntar.
Pd
1
Deveria saber que abrir, executar uma consulta e descartar uma conexão, mesmo em um loop, é igualmente rápido e, às vezes, MAIS RÁPIDO do que abrir uma vez e fazer um loop na consulta. Sempre apenas descarte. É mais seguro e RÁPIDO. Não se preocupe com a sobrecarga de obter uma conexão da piscina - é tão trivial.
smdrager
38

A melhor prática é criar uma conexão por consulta - e, no caso de exibição de dados, a melhor prática é fazer com que a consulta traga todos os dados necessários de uma só vez.

Informações básicas:

No .NET, as chamadas SqlConnection.Open()por padrão sempre usam transparentemente o pool de conexões (consulte "Usando o pool de conexões com o SQL Server" no MSDN). Assim, você pode simplesmente pegar uma nova conexão usando Open()e ligar Close()quando terminar, e o .NET fará a coisa certa.

Observe que, sem o pool de conexões, uma conexão por consulta seria uma péssima idéia, pois a criação de conexões reais com o banco de dados pode ser muito onerosa (autenticação, sobrecarga de rede etc.), e o número de conexões abertas simultâneas geralmente é muito limitado.

Oded
fonte
7
@webnoob - Como o .NET usa o pool de conexões, não, não. O motivo é que as conexões podem ser fechadas, realocadas etc. - portanto, reutilizar uma conexão não é uma boa prática.
Oded
11
-1 A resposta é bastante enganadora. Criando uma conexão por consulta é uma muito má ideia. O que você provavelmente quer dizer é "recuperar uma nova conexão para cada consulta do pool de conexões" - mas isso não é o mesmo que criar uma conexão.
sleske
1
@sleske - Qual é a diferença da resposta por pdr?
Oded
3
@Oded: Ah, entendo. No .NET, a chamada SqlConnection.Open()sempre usará de maneira transparente o pool de conexões. Portanto, a diferença entre "abrir uma conexão" e "recuperar novamente uma conexão de um pool" não existe. Meu mal-entendido. Tomei a liberdade de editar uma pequena explicação para a questão e retomei a votação.
sleske
2
@ eaglei22 - certamente deve fazê-lo (consulte docs.microsoft.com/en-us/dotnet/framework/data/adonet/… ). Em geral, convém retornar a conexão ao pool o mais rápido possível; no entanto, se você estiver emitindo várias consultas em sequência, talvez seja melhor reutilizar uma conexão conforme sugerido. Você precisaria testar e ver qual abordagem é melhor para você (não sei quais critérios você usa - verifique nos dois sentidos e veja o efeito nas métricas escolhidas).
Oded
0

Lembre-se de tudo isso no contexto do ecossistema .Net.

Às vezes, os desenvolvedores desejam "otimizar" seu código para reutilizar seus objetos de conexão. Dado o contexto desta questão, isso quase sempre é um erro.

O ADO.Net possui um recurso chamado Pool de Conexão . Quando você cria e abre um novo objeto de conexão, o que realmente está fazendo é solicitar uma conexão de um pool. Quando você fecha uma conexão, você a retorna para a piscina.

É importante entender os objetos que usamos diretamente no código: SqlConnection, MySqlConnection, OleDbConnectio, etc, são apenas invólucros em torno de uma conexão subjacente real gerenciada pelo ADO.Net, e as conexões reais do ADO.Net são muito "mais pesadas" e mais caras do ponto de vista de desempenho. São esses objetos subjacentes que têm preocupações como autenticação, trânsito de rede, criptografia e essas coisas superam a pequena quantidade de memória no objeto que você realmente vê em seu próprio código.

Ao tentar reutilizar o objeto de conexão, você interrompe a capacidade do ADO.Net de gerenciar efetivamente as conexões subjacentes importantes. Você ganha eficiência na coisa pequena à custa da coisa muito maior.

A reutilização de uma conexão através de um aplicativo ou solicitação http também pode forçá-lo a serializar acidentalmente algo que poderia ser executado em paralelo e tornar-se um gargalo de desempenho. Eu já vi isso acontecer em aplicações reais.

No caso do exemplo de página da Web aqui, em que você mantém pelo menos apenas a pequena conexão durante a duração de uma única solicitação / resposta http, você pode obter ainda mais eficiência avaliando quais consultas são executadas em seu pipeline de solicitação e tenta obter eles até o menor número possível de solicitações separadas para o banco de dados (dica: você pode enviar mais de uma consulta em uma única string SQL e usar DataReader.NextResult()ou verificar tabelas diferentes em DataSetpara se mover entre elas).

Em outras palavras, em vez de pensar em reutilizar uma conexão para um aplicativo ou solicitação de HTTP versus uma conexão por consulta, pense em termos de uma conexão para cada vez que você chamar o banco de dados ... a cada ida e volta. Em seguida, tente minimizar o número de conexões, minimizando o número dessas viagens. Dessa forma, você pode satisfazer os dois objetivos.


Mas isso é apenas um tipo de otimização. Há também a otimização do tempo do programador e a reutilização efetiva do código. Os desenvolvedores não querem escrever repetidamente o mesmo código padrão apenas para obter um objeto de conexão aberto e pronto para uso. Não é apenas entediante, é uma maneira de introduzir bugs em um programa.

Mesmo aqui, porém, geralmente é melhor ter uma conexão por consulta (ou ida e volta). Existem outros padrões que você pode usar para ajudar a evitar a reescrita do mesmo código padrão. Aqui está um exemplo que eu gosto, mas existem muitos outros.

Joel Coehoorn
fonte
Estou atrasado para esta festa, mas acho que esta resposta cobre alguns pontos importantes :)
Joel Coehoorn