MySQL ou MariaDB lento? Ative o slow query log, resuma as piores consultas com pt-query-digest e leia o EXPLAIN para saber o que corrigir.

Para que serve

Quando você tem um MySQL lento, a causa quase sempre está em poucas consultas que leem linhas demais, ordenam em disco ou esperam por bloqueios. Este artigo mostra como capturar essas consultas com o slow query log, ordenar as piores e interpretar o EXPLAIN para decidir se falta índice ou se a consulta precisa ser reescrita. Vale para MySQL 8.0/8.4 e MariaDB 10.11/11.x.

Se o problema for memória mal dimensionada (buffer pool pequeno, por exemplo), veja também o artigo sobre tuning básico de MySQL para sites, na categoria Web e hospedagem. Aqui o foco é encontrar a consulta culpada.

Pré-requisitos

  • Acesso root ao servidor (ou à VM do banco) e a um usuário MySQL com privilégio administrativo.
  • Alguns GB livres no disco onde o log será gravado.
  • Opcional: pacote percona-toolkit (disponível nos repositórios do Debian e do Ubuntu) para o pt-query-digest.

1. MySQL lento: confirme que o banco é mesmo o gargalo

  1. Rode top ou htop e veja se o processo mysqld/mariadbd consome CPU, ou se o wa (espera de I/O) está alto.
  2. Veja o que está rodando agora:
    mysql -e "SHOW FULL PROCESSLIST;"
    Consultas com Time alto e estado Sending data, Creating sort index ou Copying to tmp table já são suspeitas. Estado Waiting for ... lock indica bloqueio: nesse caso, veja o artigo sobre deadlocks e locks desta categoria.

2. Ative o slow query log do MySQL

Dá para ligar sem reiniciar o serviço:

SET GLOBAL slow_query_log = ON;
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = ON;
SET GLOBAL log_throttle_queries_not_using_indexes = 60;
  • long_query_time é em segundos e aceita frações (ex.: 0.5). Comece com 1 segundo; em sistemas OLTP rápidos, 0.2 a 0.5 revela mais.
  • O novo long_query_time só vale para conexões abertas depois do comando. Aplicações com pool de conexões podem demorar a refletir.
  • log_throttle_queries_not_using_indexes (só no MySQL) limita quantas consultas "sem índice" entram por minuto, para o log não explodir. No MariaDB, omita essa linha.

Para manter após reinício, grave no arquivo de configuração. No Debian/Ubuntu: /etc/mysql/mysql.conf.d/mysqld.cnf (MySQL) ou /etc/mysql/mariadb.conf.d/50-server.cnf (MariaDB), dentro de [mysqld]:

slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
log_throttle_queries_not_using_indexes = 60

No MySQL 8 também é possível usar SET PERSIST no lugar de SET GLOBAL, que grava o valor em mysqld-auto.cnf. No MariaDB os nomes novos são log_slow_query e log_slow_query_time, mas os nomes antigos continuam funcionando como sinônimos.

Atenção: não deixe long_query_time = 0 por muito tempo em produção. Isso grava todas as consultas e pode encher o disco em poucas horas. Use 0 só por alguns minutos, para amostragem.

3. Resuma o log e escolha o que atacar

Deixe o log coletar durante um período representativo (horário de pico). Depois, gere um ranking:

# Ferramenta que vem com o MySQL: top 10 por tempo total
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log

# Mais completo: agrupa consultas iguais e mostra percentis
apt install percona-toolkit
pt-query-digest /var/log/mysql/mysql-slow.log > /root/relatorio-slow.txt

No relatório do pt-query-digest, a primeira tabela ("Profile") ordena as consultas pelo tempo total gasto. Priorize por essa coluna: uma consulta de 0,3 s executada 50 mil vezes por hora pesa mais que uma de 20 s executada uma vez. Observe também Rows examine versus Rows sent: examinar 2 milhões de linhas para devolver 10 é sinal clássico de índice ausente.

4. Leia o EXPLAIN da consulta lenta

Copie a consulta do relatório, troque os valores de exemplo por valores reais e coloque EXPLAIN na frente:

EXPLAIN SELECT id, total FROM pedidos
WHERE cliente_id = 4821 AND status = 'aberto'
ORDER BY criado_em DESC LIMIT 20;

As colunas que mais importam:

ColunaO que observar
typeDo pior para o melhor: ALL (varre a tabela inteira), index (varre o índice inteiro), range, ref, eq_ref, const. ALL em tabela grande é o primeiro alvo.
keyÍndice escolhido. NULL significa nenhum.
possible_keysÍndices candidatos. Se há candidato mas key é NULL, o otimizador achou o índice pouco seletivo.
rows e filteredEstimativa de linhas lidas e a porcentagem que sobra após o filtro. rows alto com filtered baixo = desperdício.
ExtraUsing filesort (ordenação fora do índice) e Using temporary (tabela temporária) custam caro em volume. Using index é bom: a consulta foi respondida só pelo índice.

Para ver o tempo real de cada etapa, use EXPLAIN ANALYZE no MySQL 8 (executa a consulta de verdade) ou ANALYZE FORMAT=JSON SELECT ... no MariaDB. Não rode essas variantes com UPDATE/DELETE em produção sem cuidado, porque elas executam o comando.

5. Corrija a causa

  • Índice ausente: no exemplo acima, um índice composto (cliente_id, status, criado_em) resolve o filtro e a ordenação. Veja o artigo sobre índices: quando criar e como achar os que faltam.
  • Função na coluna: WHERE DATE(criado_em) = '2026-05-01' impede o uso de índice. Prefira um intervalo: criado_em >= '2026-05-01' AND criado_em < '2026-05-02'.
  • Tipos diferentes: comparar coluna VARCHAR com número (WHERE cpf = 12345678900) força conversão e ignora o índice. Use aspas.
  • LIKE '%texto': curinga no início não usa índice B-tree. Avalie índice FULLTEXT ou outra estratégia de busca.
  • Paginação com OFFSET grande: LIMIT 20 OFFSET 200000 lê e descarta 200 mil linhas. Pagine pela chave (WHERE id < último_id ORDER BY id DESC LIMIT 20).
  • SELECT * em tabelas largas: traga só as colunas necessárias; isso também permite índices de cobertura.

Como saber se funcionou

  • O EXPLAIN mostra type diferente de ALL, um índice em key e rows muito menor.
  • A consulta some (ou cai de posição) no próximo relatório do pt-query-digest.
  • O tempo de resposta da aplicação e o uso de CPU/I/O do servidor caem no horário de pico.

Problemas comuns

O arquivo de log não é criado

O diretório precisa pertencer ao usuário mysql. No Ubuntu, o AppArmor só permite gravação em caminhos previstos no perfil; use /var/log/mysql/. Veja erros em journalctl -u mysql ou journalctl -u mariadb.

O log cresce sem parar

Confira se existe regra em /etc/logrotate.d/ para o arquivo (o pacote do MySQL/MariaDB costuma trazer uma para /var/log/mysql/*.log). Depois da análise, você pode subir long_query_time ou desligar o log.

A consulta é rápida no meu teste, mas lenta na aplicação

Pode ser bloqueio (outra transação segurando linhas), cache frio ou parâmetros diferentes. Teste com os mesmos valores que aparecem no log e confira o processlist no momento da lentidão.

Perguntas frequentes

O slow query log deixa o MySQL mais lento?

Com long_query_time de 0,5 a 1 segundo o impacto é desprezível, porque poucas consultas são gravadas. O custo só aparece com limite 0 em servidores muito movimentados.

Qual o valor ideal de long_query_time?

Comece em 1 segundo para achar os casos graves. Depois de corrigi-los, desça para 0,2 a 0,5 para pegar as consultas frequentes que somam muito tempo.

EXPLAIN funciona com UPDATE e DELETE?

Sim. EXPLAIN UPDATE ... mostra o plano sem alterar dados. Só EXPLAIN ANALYZE executa de fato o comando.

Existe alternativa ao arquivo de log?

Sim: o Performance Schema guarda estatísticas por tipo de consulta. Veja o artigo sobre monitorar banco de dados desta categoria.

Leitura complementar

Precisa de ajuda?

Se o servidor continuar lento mesmo após essas verificações, abra um ticket informando o horário do problema e anexando o relatório do pt-query-digest. A otimização do banco é responsabilidade do cliente sem o serviço de Gerenciamento, mas podemos verificar se há problema de hardware, disco ou rede.

Esta resposta lhe foi útil? 0 Usuários acharam útil (0 Votos)

Leia também