Uma coisa que sempre tive dúvidas era sobre como o package database/sql geria as conexões com banco de dados. A forma como eu costumava trabalhar era abrir uma nova conexão a cada request recebida, fechando-a ao final.
Mas será que essa é a melhor forma?
A resposta curta é NÃO. Mas como eu sei que você, assim como eu não se satisfaz com uma resposta simples dessa, vamos entender melhor como o package padrão do Go gerencia as conexões.
A primeira coisa que precisamos ter em mente, é que sempre que abrimos uma nova conexão utilizando o sql.Open
, ele nos retornará uma struct do tipo *sql.DB
.
Essa struct, além de ser segura para ser utilizada em múltiplas goroutines, ou seja, “thread-safe”, ela não lida somente com uma conexão, mas sim com um pool de conexões.
Sempre que executamos o método Query
ou Exec
, a struct sql.DB
irá retornar uma conexão disponível. Caso não tenha uma disponível, ela irá abrir uma nova e retorna-la.
O sql.DB
pode abrir um número ilimitado de conexões com seu banco de dados. Se isso se tornar um problema em seu sistema, é possível limitar a quantidade de conexões, utilizando o método sql.DB.SetMaxOpenConns
. Só não se esqueça que limitar o número de conexões que podem ser abertas é praticamente criar um semáforo para acesso ao banco de dados. Isso por que novas requests podem ter que esperar a liberação de uma conexão para que possam ser executadas.
Após a execução do comando, a conexão voltará para o pool do sql.DB
. Por padrão sempre serão mantidas duas conexões ativas, porém caso seu sistema tenha um número elevado de transações em simultâneo, você pode elevar esse valor utilizando o método sql.DB.SetMaxIdleConns
.
Vale a nota que essa configuração não irá limitar a quantidade de conexões que podem ser aberta, mas sim as que serão mantidas abertas.
Caso você resolva fazer essa mudança no número máximo de conexões que podem ficar abertas em seu sistema, é muito importante definir um tempo máximo de vida para essas conexões em seu pool, pois caso você não o faça, elas ficarão lá até seu sistema ser reiniciado.
Para definir esse tempo, utilize o método sql.DB.SetConnMaxIdleTime
.
Uma outra configuração que podemos mudar na gestão das conexões é o tempo de vida de uma conexão aberta. Para definir esse tempo, utilizamos o método sql.DB.SetConnMaxLifetime
.
Embora parecidos, sql.DB.SetConnMaxIdleTime
e sql.DB.SetConnMaxLifetime
tem papéis bem diferentes. O primeiro diz quanto tempo uma conexão pode ficar no pool sem ser utilizada, enquanto o segundo diz quanto tempo uma conexão pode ficar aberta.
Agora que você conhece um pouco melhor como o package database/sql lida com conexões, recomendo utilizar o método sql.DB.Stats
de tempos em tempos e logar a informação retornada em algum lugar.
Esse método irá te trazer informações como:
- Número de conexões ativas;
- Número de conexões em uso;
- Número de conexões no pool;
- Tempo de espera por uma conexão.
Com essas informações em mãos, com certeza você conseguirá otimizar a comunicação entre seu sistema e o banco de dados.
Deixe suas dúvidas nos comentários.
Até a próxima!
Show! Era uma dúvida minha essa questão do pool de conexões. Esse comportamento do pool pode ser alterado pelo driver específico do banco?
Depende do drive. Se você usar ele diretamente com o sql.Open, as mudanças possíveis são somente as que listo aqui no post.