Abrir uma conexão de banco de dados é uma operação cara, e o pool de conexões é usado para manter as conexões de banco de dados abertas para que possam ser reutilizadas. Isso evita ter que abrir repetidamente sessões de rede, autenticar e verificar a autorização. O pooling mantém as conexões ativas para que, quando uma conexão for solicitada posteriormente, uma das ativas seja usada em vez de ter que criar uma do zero.
Agrupamento de conexões
O pool de conexões tornou-se um dos métodos mais comuns de manipulação de conexões de banco de dados antes de uma solicitação de consulta. Normalmente pensamos que uma conexão com o banco de dados é rápida, mas esse não é o caso, especialmente quando um grande número de clientes está se conectando. Sem o pool de conexões, uma solicitação levaria de 35 a 50 ms para se conectar, mas de 1 a 2 ms se o pool de conexões for empregado. O pool de conexões está, portanto, pré-alocando conexões de banco de dados e, em seguida, reciclando-as quando novos clientes estão se conectando
Motivos para o pool de conexões
- Para evitar travar seu servidor. Os servidores PostgreSQL são limitados a um número de clientes que eles tratam por vez, dependendo do parâmetro de memória. Se esse número for ultrapassado, você acabará travando o servidor. Com o pool de conexões, os clientes usam um número definido de conexões.
- Facilite o processamento de consultas. Normalmente, as requisições de banco de dados são executadas de forma serial com um critério de primeiro a entrar, primeiro a sair. Com um grande conjunto de clientes, isso levaria séculos para alcançar esse processo. Portanto, a abordagem deve ser fazer uma única conexão com solicitações em pipeline que podem ser executadas simultaneamente, em vez de uma de cada vez.
- Melhore a segurança. Muitas vezes, uma conexão envolve um handshake que pode levar de 25 a 35 ms, em média, durante o qual um SSL é estabelecido, as senhas são verificadas e o compartilhamento das informações de configuração. Todo esse trabalho para cada usuário conectado resultará em uso extensivo de memória. No entanto, com o pool de conexões, o número de conexões é reduzido, economizando memória.
Tipos de pool de conexões
Existem basicamente dois tipos de pool de conexão, no entanto, há um terceiro tipo de solução alternativa que atua como uma estratégia de pool de conexão conhecida como conexões persistentes.
Agrupamento de conexões persistentes
Essa abordagem visa manter uma conexão inicial ativa desde o momento em que é iniciada. Ele não contém totalmente os recursos de pool de conexão, mas o suficiente para fornecer alguma conexão contínua. É bastante útil para um pequeno conjunto de conexões de clientes cuja sobrecarga pode variar entre 25-50 ms. Uma limitação dessa abordagem é que ela é limitada a um número de conexões ao banco de dados com normalmente uma única conexão por entrada ao servidor.
Agrupamento de conexões de estrutura
O pool de conexões de estrutura ocorre em um nível de aplicativo pelo qual, sempre que o script do servidor é iniciado, um pool de conexões é estabelecido para lidar com solicitações de consulta que chegarão mais tarde.
Agrupamento de conexões autônomas
Para cada conexão com o banco de dados, uma memória de sobrecarga entre 5 e 10 MB é usada para atender a uma solicitação de consulta. Isso não é muito bom para um grande número de conexões. O uso do pool de conexões da estrutura pode ser limitado por esse número de conexões, pois pode ocorrer um uso de grande tamanho de memória. Optamos, assim, por usar o pool de conexões Standalone que é configurado de acordo com as sessões, declarações e transações do Postgres. A principal vantagem associada a essa abordagem é:custo de overhead mínimo de cerca de 2kb para cada conexão.
Ao criar uma classe de pool de conexões, ela deve atender aos seguintes fatores para aumentar o desempenho do banco de dados:
- Pré-alocar as conexões
- Visualize as conexões disponíveis
- Atribuir novas conexões
- Aguarde a disponibilidade de uma conexão
- Fechar conexão
Pré-alocação das conexões
Garantir mais conexões com antecedência facilitará o tratamento de solicitações no momento em que o aplicativo foi iniciado. Por exemplo, se seu servidor for desenvolvido com Java, você pode usar vetores para armazenar conexões inativas disponíveis usando o código abaixo.
availableConnections = new Vector(connections);
busyConnections = new Vector();
for(int i=0; i<connections; i++) {
availableConnections.addElement(makeNewConnection());
}
Supervisionando as conexões disponíveis
A classe deve ser capaz de verificar qualquer conexão ociosa em uma lista de conexões ocupadas e retorná-la. Isso é feito basicamente para reutilizar uma conexão ou fechar conexões que não estão em uso. Às vezes, as conexões expiram, portanto, ao retornar uma conexão, é muito importante verificar se ela ainda está aberta. Caso contrário, você precisará descartar essa conexão e repetir o processo. Quando uma conexão é descartada, é aberto um slot que pode ser usado para processar uma nova conexão quando o limite for atingido. Isso pode ser alcançado com
public synchronized Connection getConnection() throws SQLException {
if (!availableConnections.isEmpty()) { Connection existingConnection =
(Connection)availableConnections.lastElement(); int lastIndex = availableConnections.size() - 1; availableConnections.removeElementAt(lastIndex); if (existingConnection.isClosed()) {
notifyAll(); // Freed up a spot for anybody waiting.
return(getConnection()); // Repeat process. } else {
busyConnections.addElement(existingConnection);
return(existingConnection); }
} }
Atribuindo uma nova conexão
Você deve ser capaz de iniciar um thread em segundo plano para atribuir uma nova conexão se não houver inatividade disponível e se o limite de conexão estiver quase sendo atingido.
if ((totalConnections() < maxConnections) && !connectionPending) { // Pending = connecting in bg
makeBackgroundConnection(); }
try {
wait(); // Give up lock and suspend self.
} catch(InterruptedException ie) {} return(getConnection()); // Try again.
Aguardando uma nova conexão
Quando não há conexão ociosa e o limite de conexão foi atingido, a configuração deve poder aguardar a disponibilidade de uma nova conexão sem pool contínuo. Podemos fazer isso usando o método wait que fornece um bloqueio de sincronização de thread e suspende o thread até que uma notificação seja fornecida.
try {
wait();
} catch(InterruptedException ie) {}
return(getConnection());
Para uma boa ética do aplicativo, os clientes não devem esperar em tempo real por uma conexão, em vez disso, você lançará uma exceção quando as conexões estiverem ausentes com o código abaixo:
throw new SQLException("Connection limit reached");
Fechando a conexão
Quando as conexões são coletadas como lixo, você deve fechá-las em vez de fazê-lo explicitamente. No entanto, se você quiser uma abordagem explícita para fechar uma conexão, poderá usar:
public synchronized void closeAllConnections() {
// The closeConnections method loops down Vector, calling // close and ignoring any exceptions thrown. closeConnections(availableConnections); availableConnections = new Vector(); closeConnections(busyConnections);
busyConnections = new Vector();
}
Conclusão
O pool de conexões para PostgreSQL nos ajuda a reduzir o número de recursos necessários para a conexão com o banco de dados e melhora a velocidade de conectividade com o banco de dados. Isso é conseguido agrupando conexões com o BD, mantendo essas conexões e consequentemente reduzindo o número de conexões que devem ser abertas.