Como identificar gargalos em sua aplicação web

Meu site está lento. Como descobrir o problema?

Seu site demora para carregar, fica indisponível ou apresenta erros 502 e 504? Antes de alterar a hospedagem ou aumentar os recursos do servidor, investigue o que está causando a lentidão.

Neste artigo, vamos usar o htop para observar o consumo de CPU e memória e os processos em execução. Depois, vamos aprofundar a análise no MySQL, PHP-FPM, Nginx e Apache, cruzando consultas e logs para identificar possíveis gargalos.

Em indisponibilidade, comece pelo estado dos serviços

Confira o estado do servidor web, do PHP-FPM e do MySQL, conforme a arquitetura da aplicação. Se encontrar um serviço fora de operação, investigue sua falha e trabalhe para restabelecê-lo. Se estiverem ativos, prossiga com o diagnóstico, pois o estado do serviço sozinho não comprova que a aplicação funciona.

Esse primeiro passo se aplica à indisponibilidade. Para investigar lentidão, observe os recursos e as operações em andamento.

Observe o servidor durante a lentidão

O htop permite verificar quais processos estão consumindo CPU e memória naquele momento. Ordene por CPU com P ou por memória com M e observe se o consumo coincide com a lentidão da aplicação.

Um processo PHP com CPU elevada direciona a investigação para o processamento da aplicação. O MySQL em destaque pede análise das consultas e esperas do banco. Já o crescimento de processos ou consumo do servidor web deve ser confrontado com seus logs de acesso e erro.

Registre o horário da observação. Uma captura isolada mostra o estado daquele instante, não o histórico nem a causa do problema.

htop com um processo de laboratório próximo de 100% de CPU e outro usando cerca de 521 MiB de memória residente.
Captura de laboratório do htop: um processo de demonstração ocupa aproximadamente um núcleo. A lista mostra apenas os processos do teste; os indicadores superiores representam o servidor completo. O consumo observado orienta a investigação, mas não identifica a requisição nem comprova saturação de todo o servidor.
Ver captura em tamanho original (nova aba)

Cruze os registros antes de atribuir a causa

Uma falha registrada pelo Nginx pode ser consequência de um problema no PHP-FPM. Um processamento PHP demorado pode estar aguardando uma consulta no MySQL. Por isso, encontrar uma mensagem de erro é o início da investigação.

Delimite o intervalo do incidente e compare os registros das camadas envolvidas. Observe horários, mensagens recorrentes, URLs e identificadores de requisição, quando disponíveis. Essa correlação ajuda a reconstruir o que aconteceu e a testar hipóteses sobre a origem da falha.

MySQL: descubra o que a aplicação está aguardando

Quando o PHP demora para responder, parte desse tempo pode estar sendo consumida na comunicação com o MySQL. Para investigar, separe três situações:

  • Serviço indisponível ou reiniciando: examine o log de erros.
  • Aplicação aguardando respostas do banco: consulte as operações e os estados das conexões durante o incidente.
  • Operações recorrentemente demoradas: procure consultas registradas no slow query log, caso esteja habilitado.

Verifique as operações durante a lentidão

Se o htop mostrar alto consumo de memória pelo MySQL durante a lentidão, aprofunde a investigação com o mytop. Acompanhe as consultas em execução e os estados das conexões para identificar o que merece análise.

Ilustração da interface do mytop: seis conexões em Sleep em branco e três consultas em execução em amarelo.
Ilustração do mytop com conexões ociosas em branco e consultas em execução em amarelo. As linhas são exemplos ilustrativos, não uma captura do ambiente local.
Ver imagem em tamanho original (nova aba)

Se precisar de mais detalhes sobre as conexões e as consultas em execução, execute no cliente MySQL:

SHOW FULL PROCESSLIST;

Interprete os campos em conjunto:

Campo O que observar
Id Identificador da conexão no MySQL, diferente do PID exibido pelo htop.
Command Atividade da conexão. Query indica processamento de um comando; Sleep indica uma sessão ociosa.
Time Tempo em segundos no estado atual, não necessariamente tempo de execução de uma consulta.
State Etapa de processamento ou condição de espera.
Info SQL em execução, quando disponível.

Procure operações que permanecem demoradamente no mesmo estado, comandos recorrentes e indicações de espera por bloqueios. Compare o banco, a origem da conexão e o SQL com a operação afetada na aplicação.

Uma conexão em Sleep não está executando uma consulta, mas isso não garante ausência de transações abertas ou bloqueios. A visibilidade das conexões de outros usuários depende das permissões da conta. Referência do MySQL

Localize e consulte o log de erros

Confirme o destino configurado:

SHOW GLOBAL VARIABLES LIKE 'log_error';

Caminhos comuns incluem:

/var/log/mysql/error.log
/var/log/mysqld.log

O caminho varia conforme a instalação. Se o arquivo confirmado for /var/log/mysql/error.log, consulte as últimas linhas no terminal Linux:

sudo tail -n 100 /var/log/mysql/error.log

Para acompanhar novas mensagens durante a ocorrência:

sudo tail -F /var/log/mysql/error.log

Procure eventos no horário do incidente: inicializações, encerramentos, recuperação após falhas e erros reportados pelo InnoDB. A ausência de mensagens nesse arquivo não descarta problemas de desempenho. Erros retornados às consultas também podem precisar ser investigados nos logs da aplicação.

Se log_error indicar stderr, consulte o destino que recebe essa saída, como o journal do serviço ou os logs do container. Configuração do log de erros

Em instalações com systemd que registrem esses eventos no journal, por exemplo:

sudo journalctl -u mysql.service \
  --since '15 minutes ago' \
  --no-pager

A unidade pode se chamar mysqld.service; utilize o nome correspondente à instalação. Referência do journalctl

Verifique o registro de consultas lentas

Consulte a configuração:

SHOW GLOBAL VARIABLES
WHERE Variable_name IN (
    'slow_query_log',
    'slow_query_log_file',
    'log_output',
    'long_query_time'
);
  • slow_query_log: informa se o registro está habilitado.
  • slow_query_log_file: indica o arquivo configurado para a saída em arquivo.
  • log_output: define destinos como FILE, TABLE ou ambos.
  • long_query_time: define o limite de duração utilizado como um dos critérios de registro.

Quando houver saída em arquivo, examine o caminho retornado. Procure consultas recorrentes e compare campos como Query_time, Lock_time, Rows_sent e Rows_examined. Uma quantidade elevada de linhas examinadas em relação às retornadas é uma pista para investigar o plano de execução, não uma confirmação automática de índice ausente.

O slow query log vem desabilitado por padrão. Se não estava ativo durante o incidente, ele não terá registrado retroativamente as consultas daquele período. Referência do slow query log

PHP-FPM: descubra por que as requisições permanecem ocupadas

Uma requisição PHP pode demorar por processamento da própria aplicação ou por espera de banco de dados e serviços externos. Enquanto isso, ela pode manter um worker ocupado. Investigue os registros do pool e da aplicação para entender se existem falhas, processamento prolongado ou acúmulo de trabalho. Relacione esses eventos aos erros e tempos observados no servidor web.

Localize o serviço e os arquivos de log

Em instalações com systemd, liste as unidades correspondentes:

systemctl list-units --all --type=service 'php*-fpm.service'

Utilize o nome encontrado no lugar de <UNIDADE_PHP_FPM>:

sudo journalctl -u '<UNIDADE_PHP_FPM>' \
  --since '15 minutes ago' \
  --no-pager

O journal pode conter eventos do serviço sem concentrar todos os erros da aplicação.

Caminhos comuns, dependentes da instalação, incluem:

/var/log/php8.4-fpm.log
/var/log/php-fpm/error.log

O primeiro é um exemplo de nome com versão. Confirme o destino em error_log, na configuração do FPM, e verifique possíveis definições de php_admin_value[error_log] nos pools. O PHP também possui configuração própria de registro de erros. Configuração de erros do PHP

Se o caminho confirmado for /var/log/php8.4-fpm.log:

sudo tail -n 100 /var/log/php8.4-fpm.log

Interprete as mensagens antes de alterar limites

Sinal encontrado O que investigar
server reached pm.max_children O pool atingiu seu limite de processos. Verifique duração das requisições, concorrência e capacidade disponível. Dependendo da demanda e dos recursos do servidor, pode ser necessário aumentar o número de workers por meio de pm.max_children.
Allowed memory size ... exhausted A execução atingiu o limite de memória do PHP. Isso não comprova esgotamento da RAM do servidor.
PHP Fatal error ou exceção não tratada Examine a mensagem completa, o arquivo, a linha e o contexto nos logs da aplicação.

O memory_limit limita a memória que um script pode alocar; ele não representa a memória total disponível no host. Diretivas do PHP

Consulte o slowlog quando houver processamento demorado

No pool correspondente, verifique as diretivas slowlog e request_slowlog_timeout. Quando configurado e operacional, esse mecanismo registra um backtrace de requisições que ultrapassam o tempo definido, ajudando a localizar o trecho de execução.

Com request_slowlog_timeout = 0, o mecanismo está desabilitado. O slowlog não reconstitui requisições anteriores à sua ativação.

Se o pool atinge pm.max_children com frequência e há CPU e memória disponíveis, pode ser necessário aumentar o número de workers para atender à concorrência da aplicação. Antes do ajuste, investigue a duração das requisições e possíveis esperas por banco de dados ou serviços externos. Mais workers permitem maior concorrência, mas também podem elevar o consumo de memória e a carga sobre essas dependências. Configuração do PHP-FPM

Precisa de apoio nesse diagnóstico? Converse sobre sua aplicação.

Nginx e Apache: diferencie falhas de atendimento e aumento de tráfego

Quando a aplicação apresenta erros HTTP ou fica indisponível, investigue tanto as mensagens de erro quanto o comportamento do tráfego. Falhas de conexão com o PHP-FPM, respostas demoradas e crescimento de requisições exigem análises diferentes.

Uma falha registrada pelo servidor web pode começar em outra camada. O Nginx pode aguardar uma resposta do PHP-FPM enquanto o PHP espera pelo MySQL. Também pode haver problemas de conexão, permissões ou excesso de requisições. Cruze os logs de acesso e erro com os registros dos serviços envolvidos para identificar onde o atendimento falhou e direcionar a investigação.

Localize os logs utilizados pelo site

Estes são caminhos comuns, sujeitos à configuração da instalação:

Instalação Log de acesso Log de erro
Nginx /var/log/nginx/access.log /var/log/nginx/error.log
Apache em Debian/Ubuntu /var/log/apache2/access.log /var/log/apache2/error.log
Apache em distribuições da família RHEL /var/log/httpd/access_log /var/log/httpd/error_log

No Nginx, confirme as diretivas access_log e error_log. No Apache, consulte CustomLog e ErrorLog. Um site pode ter arquivos próprios, diferentes dos logs gerais do servidor. Logs do Nginx, logs do Apache.

Com os caminhos confirmados, acompanhe os registros durante o problema.

No Nginx:

sudo tail -F \
  /var/log/nginx/access.log \
  /var/log/nginx/error.log

No Apache, usando os caminhos do exemplo Debian/Ubuntu:

sudo tail -F \
  /var/log/apache2/access.log \
  /var/log/apache2/error.log

Interprete as falhas de comunicação com a aplicação

No Nginx, observe a mensagem completa, especialmente a operação que estava sendo realizada:

Mensagem ou trecho Direção da investigação
connect() failed ... Connection refused Verifique se o serviço de destino está ativo e aceita conexões no endereço e porta configurados.
No such file or directory ao conectar ao upstream Verifique se o socket configurado existe e corresponde ao utilizado pelo PHP-FPM.
Permission denied ao conectar ao upstream Examine permissões do socket e controles de acesso do sistema.
upstream timed out Identifique se o timeout ocorreu ao conectar, enviar a requisição ou aguardar a resposta. Cruze com os registros da aplicação.

Essas mensagens orientam hipóteses. Um timeout não comprova, isoladamente, que falta capacidade no Nginx: a espera pode estar no PHP, no banco ou em uma dependência externa. O módulo FastCGI possui limites distintos para conexão, envio e leitura. Referência do FastCGI no Nginx

No Apache, examine também o módulo indicado na linha de erro. Mensagens de proxy_fcgi, quando esse módulo é utilizado, direcionam a investigação para a comunicação com o backend FastCGI.

Verifique o comportamento das requisições

No log de acesso, compare:

  • URLs que concentram requisições ou falhas.
  • Códigos HTTP recorrentes, especialmente respostas 5xx.
  • Origens e padrões de repetição.
  • Mudanças em relação ao tráfego habitual.

Para uma primeira amostragem, em um log no formato combined com o IP no primeiro campo, conte as origens nas últimas 10 mil linhas:

sudo tail -n 10000 /var/log/nginx/access.log |
  awk '{print $1}' |
  sort |
  uniq -c |
  sort -nr |
  head -n 20

Para contar os códigos HTTP nessa mesma amostra, considerando o status no nono campo:

sudo tail -n 10000 /var/log/nginx/access.log |
  awk '{print $9}' |
  sort |
  uniq -c |
  sort -nr

Esses comandos apresentam contagens na amostra, não requisições por segundo. Confirme o formato antes de utilizar as posições dos campos; logs personalizados podem ter outra estrutura. Para analisar o incidente, selecione o período correspondente, incluindo arquivos rotacionados quando necessário. Formato de logs do Nginx, formatação no Apache.

Quando investigar possível tráfego abusivo

Um crescimento abrupto de requisições, repetição intensa sobre determinadas rotas e degradação simultânea do serviço justificam investigar tráfego abusivo. Entretanto, volume elevado também pode resultar de acessos legítimos, integrações ou mecanismos de repetição da própria aplicação.

Se houver CDN, proxy ou load balancer, confirme qual endereço está sendo registrado. Muitas requisições atribuídas a um único IP podem pertencer ao intermediário, não ao cliente de origem. No Nginx, essa identificação depende da configuração de confiança dos proxies. Módulo Real IP

A confirmação de DDoS exige correlacionar esses sinais com métricas e eventos de rede ou da camada de proteção. Ataques que saturam a rede podem impedir que as requisições cheguem ao log HTTP. Tipos de ataque DDoS

Do diagnóstico ao dimensionamento da infraestrutura

Identificar o serviço envolvido é o início. O diagnóstico precisa relacionar os sintomas às consultas, ao processamento da aplicação, à configuração dos serviços e ao tráfego recebido. Com essas evidências, você pode definir as correções necessárias e avaliar se a capacidade disponível atende à demanda.

Se a investigação indicar necessidade de crescimento, o próximo passo é decidir como expandir. Essa decisão envolve entender quando sua infraestrutura precisa crescer e as diferenças entre escalonamento vertical e horizontal.

Oferecemos soluções e consultoria em infraestrutura para orientar decisões sobre servidores, hospedagem, desempenho e estabilidade.

Voltar ao blog