Poupe até 53% em Servidores VPS, escolha agora. Oferta limitada.

Solucionar lentidão no MySQL 8.0 antes que derrube sua aplicação

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:

  1. Ativar o slow query log para capturar consultas lentas
  2. Analisar o estado das conexões com SHOW FULL PROCESSLIST
  3. Verificar e ajustar o tamanho do InnoDB buffer pool
  4. Identificar tabelas sem índices com EXPLAIN
  5. Ajustar variáveis de performance dinamicamente com SET GLOBAL
  6. 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 SUPER ou SYSTEM_VARIABLES_ADMIN
  • Acesso SSH ao servidor com permissão para editar /etc/mysql/mysql.conf.d/mysqld.cnf ou /etc/my.cnf
  • Cliente MySQL instalado (mysql-client ou mariadb-client)
  • Ferramenta mysqldumpslow disponível no pacote mysql-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_size e max_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_connections e wait_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

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.

Conheça os planos de VPS da AviraHost com suporte a MySQL

  • 0 Os usuários acharam isso útil
  • MySQL, performance, slow-query, banco-de-dados, AviraHost, tuning, VPS
Esta resposta foi útil?

Artigos Relacionados

Otimizar cache Redis para aplicações PHP no Ubuntu 22.04

Para otimizar o cache Redis para aplicações PHP no Ubuntu 22.04, instale e configure o Redis,...

Configurar Alertas Automáticos com Zabbix no Ubuntu

Para configurar alertas automáticos com Zabbix no Ubuntu, instale o Zabbix Server, configure...

Otimizar MySQL: como reduzir uso de memória e acelerar consultas

Otimizar MySQL é o processo de ajustar configurações e consultas para reduzir o consumo de...

Entenda o que é Swap no Linux: como funciona e quando usar

Swap no Linux é um espaço em disco usado como extensão da memória RAM quando esta se esgota. O...

Guia Definitivo: Configurar Nginx como Proxy Reverso

Para configurar o Nginx como proxy reverso, instale o Nginx, crie um arquivo de configuração de...