Quem mantém sites e servidores em produção sabe o susto que dá receber um alerta de banco de dados fora do ar. Recentemente, investiguei um incidente clássico de exaustão de conexões no MySQL/MariaDB que mostra perfeitamente como uma aplicação legada sem manutenção pode derrubar o servidor inteiro.
Abaixo, documento o diagnóstico no terminal, o comando em lote para limpar os processos travados e as recomendações práticas para evitar a reincidência.
O sintoma inicial: erro de max_connections#
O alerta disparou indicando que o MySQL havia parado de responder a novas conexões. Ao verificar os logs, o erro clássico apareceu:
ERROR 1040 (08004): Too many connections
Quando o número de conexões ativas bate no teto global configurado no banco (@@max_connections), qualquer nova tentativa de conexão (vinda dos pools do PHP-FPM ou de outros sites) é recusada na hora.
Investigando o terminal: isolando a query ofensora#
Acessei o terminal para verificar o que estava rodando no banco de dados via processlist:
| ID | User | Host | db | Command | Time | State | Info |
| 55764331 | cliente_liveusr | localhost | cliente_livechat| Query | 5054 | Sending data | SELECT COUNT(*) AS `total`,(SELECT COUNT(*) FROM (SELECT `lz_chat_archive`.`chat_id` FROM `lz_chat_a |
| 55767542 | cliente_liveusr | localhost | cliente_livechat| Query | 5024 | Sending data | SELECT COUNT(*) AS `total`,(SELECT COUNT(*) FROM (SELECT `lz_chat_archive`.`chat_id` FROM `lz_chat_a |
| 55769351 | cliente_liveusr | localhost | cliente_livechat| Query | 5025 | Sending data | SELECT COUNT(*) AS `total`,(SELECT COUNT(*) FROM (SELECT `lz_chat_archive`.`chat_id` FROM `lz_chat_a |
O que o diagnóstico revelou:#
- Tempo de execução: Dezenas de consultas idênticas estavam presas há mais de 5.000 segundos (quase uma hora e meia cada uma).
- Estado (
Sending data): O MySQL não estava apenas esperando um lock de tabela; ele estava ativamente lendo dados pesados do disco para montar tabelas temporárias. - Aplicação: O banco pertencia ao LiveZilla (sistema de chat de atendimento).
- A consulta: Uma sequência pesada de subqueries aninhadas com
SELECT COUNT(*)iterando sobre a tabelalz_chat_archive.
O mecanismo da falha#
O LiveZilla tentava gerar estatísticas de atendimentos em tempo real no painel. Como a tabela lz_chat_archive tinha centenas de milhares de linhas e não possuía índices apropriados para aquela consulta, o MySQL criava tabelas temporárias no disco a cada clique.
A cada requisição web, o PHP abria uma nova conexão e disparava o mesmo SELECT COUNT(*). Como as consultas anteriores nunca terminavam, as novas foram se acumulando até bater no teto de conexões do MySQL, derrubando o banco para todas as aplicações do servidor.
Ação emergencial: matando conexões travadas em lote#
Com dezenas de threads travadas há milhares de segundos, dar KILL manual processo por processo via terminal demoraria demais.
Para expurgar todas as conexões presas do usuário específico de forma rápida e segura:
mysql -e "SELECT concat('KILL ', id, ';') FROM information_schema.processlist WHERE user = 'cliente_liveusr' AND time > 100" | grep -v "concat" | mysql
Como esse comando funciona:#
- Consulta a tabela
information_schema.processlistbuscando apenas threads daquele usuário com tempo superior a 100 segundos. - Monta a instrução
KILL [ID];para cada registro. - O
grep -v "concat"remove o cabeçalho gerado pelo MySQL. - O resultado é jogado de volta no próprio cliente
mysql, executando todos os encerramentos em um único lote.
Dica complementar: Se o pool do PHP-FPM continuar enviando novas conexões imediatamente após a limpeza, vale reiniciar os workers daquele usuário para cortar os sockets residuais (apenas se a conta for dedicada exclusivamente ao LiveZilla; caso contrário, reinicie apenas o pool específico ou via systemctl restart php8.x-fpm para não interromper outros sites do usuário):
killall -u cliente_liveusr php-fpm
Conclusão: recomendações práticas para resolver em definitivo#
- Expurgo e arquivamento: Limpar registros antigos da tabela
lz_chat_archiveou exportar históricos com mais de 1 ano para armazenamento frio. - Revisão de índices: Criar índices na coluna
chat_ide em colunas de datas utilizadas nos filtros estatísticos. - Desativação de estatísticas em tempo real: Configurar o painel do chat para atualizar relatórios com intervalos maiores ou sob demanda.
- Por que apenas aumentar o
max_connectionsé perigoso: Aumentar o limite sem corrigir a query apenas faz com que centenas de consultas pesadas fiquem rodando simultaneamente na memória RAM, o que pode invocar o OOM Killer do kernel Linux e derrubar o servidor por falta de memória física.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários