18 min de leitura · Guia técnico
Lentidão no MySQL 8.0 ocorre quando consultas mal otimizadas, falta de índices ou configurações inadequadas de memória sobrecarregam o servidor de banco de dados. Identificar o gargalo antes que ele derrube sua aplicação exige monitoramento ativo e ajustes precisos. Siga estes passos para diagnosticar e corrigir o problema:
- Ativar o slow query log para capturar consultas lentas
- Analisar o estado das conexões com SHOW FULL PROCESSLIST
- Verificar e ajustar o tamanho do InnoDB buffer pool
- Identificar tabelas sem índices com EXPLAIN
- Ajustar variáveis de performance dinamicamente com SET GLOBAL
- Monitorar métricas de I/O e CPU para correlacionar com picos de lentidão
Pré-requisitos para solucionar lentidão no MySQL 8.0
- MySQL 8.0 instalado e em execução (testado no Debian 12 e Rocky Linux 9)
- Acesso root ou usuário com privilégio
SUPERouSYSTEM_VARIABLES_ADMIN - Acesso SSH ao servidor com permissão para editar
/etc/mysql/mysql.conf.d/mysqld.cnfou/etc/my.cnf - Cliente MySQL instalado (
mysql-clientoumariadb-client) - Ferramenta
mysqldumpslowdisponível no pacotemysql-server - Espaço em disco suficiente para armazenar logs de queries lentas
Como ativar o slow query log para detectar lentidão no MySQL 8.0
O primeiro passo para diagnosticar degradação de performance é habilitar o registro de consultas lentas. O slow query log é a ferramenta mais direta para identificar quais queries estão consumindo tempo excessivo sem precisar interromper o serviço.
Conecte-se ao MySQL como root e execute os comandos abaixo para ativar o log dinamicamente, sem necessidade de reiniciar o serviço:
mysql -u root -p
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
SET GLOBAL log_queries_not_using_indexes = 'ON';
Após alguns minutos de operação normal da aplicação, analise o arquivo de log com o utilitário mysqldumpslow:
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
Reading mysql slow query log from /var/log/mysql/mysql-slow.log
Count: 47 Time=3.21s (150s) Lock=0.00s (0s) Rows=15234.0 (715998), app[app]@localhost
SELECT * FROM pedidos WHERE status = 'pendente' ORDER BY criado_em DESC
Count: 23 Time=2.87s (66s) Lock=0.01s (0s) Rows=8901.0 (204723), app[app]@localhost
SELECT u.*, p.* FROM usuarios u JOIN perfis p ON u.id = p.usuario_id WHERE u.ativo = 1
O campo Time mostra o tempo médio de execução e o total acumulado. Queries com alto tempo total são as candidatas prioritárias para otimização. Para persistir essas configurações após reinicialização, adicione ao arquivo de configuração principal:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
[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
Para referência sobre como acessar seu servidor e executar esses comandos remotamente, consulte o guia Acessando servidores VPS Linux da AviraHost.
Analisar conexões ativas e identificar queries travadas
Quando a aplicação apresenta travamentos ou timeouts, a contenção de recursos entre conexões simultâneas é frequentemente a causa raiz. O comando SHOW FULL PROCESSLIST oferece visibilidade imediata sobre o estado de cada thread ativa no servidor.
Execute dentro do cliente MySQL:
SHOW FULL PROCESSLIST;
+-----+------+-----------+--------+---------+------+------------------------------+------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+--------+---------+------+------------------------------+------------------------------------------+
| 142 | app | localhost | loja | Query | 187 | Waiting for table metadata lock | ALTER TABLE produtos ADD COLUMN peso... |
| 143 | app | localhost | loja | Query | 45 | Locked | UPDATE estoque SET qtd=qtd-1 WHERE... |
| 144 | app | localhost | loja | Query | 2 | Copying to tmp table | SELECT * FROM relatorios WHERE... |
+-----+------+-----------+--------+---------+------+------------------------------+------------------------------------------+
Estados críticos que indicam problemas:
- Waiting for table metadata lock: uma operação DDL (ALTER TABLE, DROP TABLE) está bloqueando todas as queries na tabela
- Locked: contenção de lock em nível de linha ou tabela entre transações concorrentes
- Copying to tmp table: query gerando tabela temporária em disco por falta de memória ou índice inadequado
- Sending data: MySQL está lendo e transferindo grandes volumes de dados — verifique se há full table scan
Para encerrar uma query problemática sem reiniciar o serviço, use o ID listado na coluna Id:
KILL QUERY 142;
Se a thread inteira precisar ser encerrada (conexão travada):
KILL 142;
Para automatizar a identificação de queries longas, use esta consulta na tabela information_schema.processlist:
SELECT id, user, host, db, time, state, LEFT(info, 100) AS query
FROM information_schema.processlist
WHERE command != 'Sleep'
AND time > 30
ORDER BY time DESC;
Ajustar o InnoDB buffer pool para reduzir leituras em disco
O dimensionamento inadequado do buffer pool é uma das causas mais comuns de lentidão persistente em servidores de banco de dados. Quando o cache de dados é insuficiente, o MySQL realiza I/O de disco para cada leitura de página, multiplicando a latência das consultas.
Verifique o tamanho atual do buffer pool e a taxa de acerto (hit ratio):
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';
+---------------------------------------+-------------+
| Variable_name | Value |
+---------------------------------------+-------------+
| Innodb_buffer_pool_read_requests | 98432156 |
| Innodb_buffer_pool_reads | 1243891 |
| Innodb_buffer_pool_pages_total | 8192 |
| Innodb_buffer_pool_pages_free | 124 |
| Innodb_buffer_pool_bytes_data | 134086656 |
+---------------------------------------+-------------+
Calcule o hit ratio com a fórmula:
Hit Ratio = (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100
Um hit ratio abaixo de 95% indica que o buffer pool está subdimensionado. Em servidores dedicados ao banco de dados, aloque entre 60% e 80% da RAM disponível. Para um servidor com 8 GB de RAM, configure:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
innodb_buffer_pool_size = 5G
innodb_buffer_pool_instances = 4
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
No MySQL 8.0, o innodb_buffer_pool_size pode ser ajustado dinamicamente sem reiniciar:
SET GLOBAL innodb_buffer_pool_size = 5368709120;
Query OK, 0 rows affected (0.00 sec)
Atenção: aumentar o buffer pool reduz a memória disponível para o sistema operacional e outras aplicações. Monitore o uso de swap após o ajuste para garantir que o servidor não entre em swapping, o que pioraria drasticamente a performance.
Para dicas adicionais de tuning no servidor Linux, veja o artigo Dicas de Otimização de Servidores Linux.
Identificar e criar índices ausentes com EXPLAIN
A ausência de índices adequados força o MySQL a realizar varredura completa da tabela (full table scan) em cada execução da query, tornando o tempo de resposta proporcional ao volume de dados. O comando EXPLAIN revela o plano de execução escolhido pelo otimizador.
Analise uma query identificada no slow query log:
EXPLAIN SELECT * FROM pedidos WHERE status = 'pendente' ORDER BY criado_em DESC\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: pedidos
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1847293
filtered: 10.00
Extra: Using where; Using filesort
Os sinais de alerta neste output são:
- type: ALL — full table scan, lendo todas as 1.847.293 linhas
- key: NULL — nenhum índice está sendo utilizado
- Using filesort — ordenação sendo feita em memória ou disco, sem índice
Crie um índice composto que cubra tanto o filtro quanto a ordenação:
CREATE INDEX idx_pedidos_status_criado ON pedidos (status, criado_em DESC);
Execute o EXPLAIN novamente para confirmar a melhoria:
EXPLAIN SELECT * FROM pedidos WHERE status = 'pendente' ORDER BY criado_em DESC\G
*************************** 1. row ***************************
type: ref
possible_keys: idx_pedidos_status_criado
key: idx_pedidos_status_criado
key_len: 203
rows: 184729
filtered: 100.00
Extra: Using index condition
O type: ref e o uso do índice confirmam que o otimizador agora utiliza o índice criado. Para tabelas em produção com alto volume de escrita, use a opção ALGORITHM=INPLACE, LOCK=NONE para criar o índice sem bloquear a tabela:
ALTER TABLE pedidos
ADD INDEX idx_pedidos_status_criado (status, criado_em DESC),
ALGORITHM=INPLACE,
LOCK=NONE;
Monitorar variáveis de status e ajustar configurações de conexão
Além do buffer pool e dos índices, variáveis relacionadas ao gerenciamento de conexões e cache de queries impactam diretamente a estabilidade sob carga. O MySQL 8.0 removeu o query cache (presente no 5.7), mas outras variáveis continuam críticas para performance em alta concorrência.
Verifique as variáveis de status mais relevantes:
SHOW GLOBAL STATUS WHERE Variable_name IN (
'Threads_connected',
'Threads_running',
'Max_used_connections',
'Aborted_connects',
'Table_open_cache_hits',
'Table_open_cache_misses',
'Created_tmp_disk_tables',
'Sort_merge_passes'
);
+-------------------------+--------+
| Variable_name | Value |
+-------------------------+--------+
| Threads_connected | 87 |
| Threads_running | 23 |
| Max_used_connections | 142 |
| Aborted_connects | 1203 |
| Table_open_cache_hits | 982341 |
| Table_open_cache_misses | 45231 |
| Created_tmp_disk_tables | 8921 |
| Sort_merge_passes | 3412 |
+-------------------------+--------+
Interpretação dos valores críticos:
- Created_tmp_disk_tables alto: queries criando tabelas temporárias em disco — aumente
tmp_table_sizeemax_heap_table_size - Sort_merge_passes alto: ordenações usando disco — aumente
sort_buffer_size - Aborted_connects alto: conexões sendo recusadas ou timeout — verifique
max_connectionsewait_timeout
Aplique os ajustes dinamicamente:
SET GLOBAL tmp_table_size = 67108864;
SET GLOBAL max_heap_table_size = 67108864;
SET GLOBAL sort_buffer_size = 4194304;
SET GLOBAL max_connections = 200;
SET GLOBAL wait_timeout = 300;
SET GLOBAL interactive_timeout = 300;
Para persistir, adicione ao mysqld.cnf:
[mysqld]
tmp_table_size = 64M
max_heap_table_size = 64M
sort_buffer_size = 4M
max_connections = 200
wait_timeout = 300
interactive_timeout = 300
Problemas comuns e como resolver
Sintoma: aplicação retorna erro "Too many connections"
Causa: o número de conexões simultâneas ultrapassou o limite definido em max_connections. Isso ocorre frequentemente quando a aplicação não utiliza pool de conexões ou quando há conexões zumbis acumuladas com estado Sleep.
Solução: aumente temporariamente o limite com SET GLOBAL max_connections = 300; e implemente um pool de conexões na aplicação (PDO com persistent connections, PgBouncer equivalente como ProxySQL). Reduza wait_timeout para encerrar conexões ociosas mais rapidamente. Verifique conexões Sleep com SELECT COUNT(*) FROM information_schema.processlist WHERE command = 'Sleep';
Sintoma: queries simples demoram mais de 5 segundos após deploy
Causa: após reinicialização do MySQL ou deploy com flush de cache, o InnoDB buffer pool está frio — todos os dados precisam ser lidos do disco. Esse fenômeno é chamado de "cold cache" e é temporário, mas pode durar minutos ou horas dependendo do volume de dados.
Solução: no MySQL 8.0, o buffer pool dump e restore é automático por padrão (innodb_buffer_pool_dump_at_shutdown=ON e innodb_buffer_pool_load_at_startup=ON). Verifique se estão ativos com SHOW VARIABLES LIKE 'innodb_buffer_pool%dump%';. Se desativados, habilite-os para que o cache seja restaurado automaticamente após reinicializações.
Sintoma: CPU do servidor em 100% durante consultas de relatório
Causa: queries analíticas com JOINs em múltiplas tabelas grandes, sem índices adequados, forçam o MySQL a processar volumes massivos de dados em memória. O uso de SELECT * agrava o problema ao transferir colunas desnecessárias.
Solução: use EXPLAIN ANALYZE (disponível no MySQL 8.0.18+) para obter o plano de execução real com tempos medidos. Substitua SELECT * por colunas específicas, adicione índices nos campos de JOIN e WHERE, e considere separar queries analíticas pesadas para uma réplica de leitura para não impactar o banco de dados principal.
Sintoma: deadlocks frequentes registrados no error log
Causa: duas ou mais transações tentam adquirir locks em ordens opostas, causando impasse. O MySQL resolve automaticamente encerrando uma das transações, mas a aplicação recebe um erro e precisa tratar o retry.
Solução: execute SHOW ENGINE INNODB STATUS\G e localize a seção LATEST DETECTED DEADLOCK para identificar as queries envolvidas. Padronize a ordem de acesso às tabelas e linhas em todas as transações da aplicação. Reduza o tempo de transações abertas e evite operações de longa duração dentro de transações.
Perguntas frequentes sobre lentidão no MySQL 8.0
Como saber se o MySQL está causando lentidão na aplicação?
Ative o slow query log no MySQL 8.0 com as variáveis slow_query_log=ON e long_query_time=1. Consultas que ultrapassam o tempo definido serão registradas em /var/log/mysql/mysql-slow.log. Use o utilitário mysqldumpslow para resumir as queries mais lentas e identificar os gargalos com precisão. Combine essa análise com monitoramento de CPU e I/O do servidor para confirmar se o banco de dados é o gargalo principal.
Qual o impacto de não ter índices nas tabelas do MySQL?
Sem índices adequados, o MySQL realiza full table scan em cada consulta, lendo todas as linhas da tabela mesmo que apenas uma seja necessária. Em tabelas com milhões de registros, isso eleva o tempo de resposta de milissegundos para segundos, aumenta o uso de CPU e pode causar travamentos em aplicações de alta concorrência. O comando EXPLAIN revela quando isso ocorre através do valor type: ALL no plano de execução.
O que é o InnoDB buffer pool e por que ele afeta a performance?
O InnoDB buffer pool é a área de memória RAM onde o MySQL armazena páginas de dados e índices em cache. Quando o buffer pool é pequeno demais, o MySQL precisa ler dados do disco com frequência, aumentando drasticamente a latência. Em servidores dedicados ao banco de dados, recomenda-se alocar entre 60% e 80% da RAM disponível para o buffer pool. Um hit ratio abaixo de 95% calculado a partir das variáveis Innodb_buffer_pool_reads e Innodb_buffer_pool_read_requests indica necessidade de aumento.
Como identificar queries que travam o MySQL com SHOW PROCESSLIST?
Execute SHOW FULL PROCESSLIST; no cliente MySQL para listar todas as conexões ativas e o estado de cada uma. Conexões com estado Locked, Waiting for table metadata lock ou Copying to tmp table indicam contenção de recursos. O campo Time mostra há quantos segundos a query está em execução, permitindo identificar processos travados. Use KILL [id] para encerrar queries problemáticas sem reiniciar o serviço.
É seguro reiniciar o MySQL para resolver lentidão em produção?
Reiniciar o MySQL em produção deve ser o último recurso, pois limpa o buffer pool e força releitura de todos os dados do disco, causando lentidão temporária adicional. Antes de reiniciar, tente encerrar apenas as queries problemáticas com KILL [id] e ajuste variáveis dinamicamente com SET GLOBAL sem necessidade de restart. Se o reinício for inevitável, certifique-se de que innodb_buffer_pool_dump_at_shutdown está ativo para acelerar a recuperação do cache.
Conclusão
- Ative o slow query log imediatamente em qualquer ambiente de produção com MySQL 8.0 — é a forma mais eficiente de identificar gargalos antes que impactem os usuários, sem overhead significativo no servidor.
- Dimensione o InnoDB buffer pool corretamente para o volume de dados da sua aplicação: um hit ratio acima de 95% é o indicador de que o cache está adequado e as leituras em disco estão minimizadas.
- Use EXPLAIN e EXPLAIN ANALYZE em todas as queries identificadas como lentas antes de criar índices — entender o plano de execução evita a criação de índices desnecessários que aumentam o custo de escrita sem benefício real.
Leia também
- Otimizar MySQL: como reduzir uso de memória e acelerar consultas
- Configurar MariaDB 11.4 no AlmaLinux 9: do padrão ao máximo
- Como Configurar Backup Automatizado do MySQL sem Plugin
Precisa de ajuda com performance de MySQL em produção?
Configurar e manter um servidor de banco de dados MySQL 8.0 com alta disponibilidade exige infraestrutura adequada e controle total sobre as configurações do sistema. Um VPS com recursos dedicados e acesso root completo permite aplicar todos os ajustes descritos neste guia sem restrições.