← Voltar para o Blog

IO lento: medir o disco antes de culpar o provedor

A aplicação ficou lenta, a CPU está ociosa e a suspeita recai sobre o disco. Antes de abrir chamado, dá para medir — e na maioria das vezes a causa está dentro da própria máquina.

Equipe EasyOps Cloud · · 8 min de leitura

A aplicação está lenta. A CPU aparece ociosa, memória sobrando, rede tranquila. Só sobra o disco — e a conclusão natural é que o armazenamento do provedor está com problema.

Às vezes está. Bem mais frequentemente, o disco está entregando o esperado e quem está pedindo demais é a própria máquina: um banco sem índice varrendo tabela, um log em modo de depuração, um backup rodando no horário de pico.

A diferença entre os dois cenários é mensurável em poucos minutos, e vale medir antes de abrir chamado — porque a conversa muda completamente quando você chega com números.

Primeiro: confirmar que é disco

O sinal mais direto vem do vmstat:

vmstat 1 10

Duas colunas respondem. A wa mostra o percentual de tempo em que a CPU ficou parada esperando entrada e saída; a b mostra quantos processos estão bloqueados nessa espera.

wa acima de 20% com us e sy baixos é o retrato de disco como gargalo. Se wa está próximo de zero, o problema é outro, e o diagnóstico de latência cobre as demais hipóteses.

O indicador de pressão dá a mesma informação com mais precisão:

cat /proc/pressure/io

some avg60 acima de 20% significa que os processos passaram mais de um quinto do último minuto parados esperando disco. É um número acionável, ao contrário do load average, que mistura CPU e disco na mesma conta.

Ler o iostat sem se enganar

sudo apt install sysstat
iostat -xz 2 5

Ignore a primeira amostra — ela é a média desde o boot. As colunas que importam:

ColunaO que éSinal de alerta
r/s, w/sOperações por segundoContexto; sozinho não diz nada
rkB/s, wkB/sVolume por segundoComparar com o esperado do disco
r_await, w_awaitLatência média, em msAcima de 20 ms em SSD é ruim
aqu-szFila médiaAcima de 2 indica saturação
%utilTempo com pedido pendenteVer ressalva abaixo

A coluna %util é a mais mal interpretada. Em disco mecânico, 100% significava saturação. Em SSD e em armazenamento de rede, que atendem várias operações em paralelo, %util em 100% apenas informa que sempre havia algo pendente — pode estar longe do limite.

O par que realmente diz é latência com fila: await alto e aqu-sz alto significa saturação real. await alto com fila baixa significa que cada operação é lenta individualmente, o que aponta para o armazenamento em si.

Como referência grosseira, um SSD saudável entrega leitura entre 0,1 e 2 ms. Acima de 10 ms de forma sustentada, há algo errado — na carga ou no disco.

Quem está escrevendo

Confirmado que há pressão, a pergunta seguinte é quem a causa:

sudo apt install iotop
sudo iotop -oPa

O -o mostra apenas processos com atividade, -P agrupa por processo em vez de thread, e -a acumula desde o início da execução — o que revela o consumidor real, em vez de quem por acaso estava escrevendo naquele instante.

Sem iotop, o kernel também informa:

sudo grep -H . /proc/*/io 2>/dev/null | grep write_bytes | \
  sort -t: -k3 -rn | head -10

Os culpados costumam ser previsíveis: banco em consulta que ordena em disco, log em modo de depuração, backup ou dump em horário ruim, e reindexação de busca.

Se for banco, vale ir direto às estatísticas dele — o artigo sobre Postgres em VPS mostra como achar a consulta que escreve arquivo temporário, que é a causa mais comum de disco saturado por banco.

Medir a capacidade real

Para saber o que o disco entrega, é preciso testar. O fio é a ferramenta certa, e exige cuidado: apontado para um dispositivo, ele sobrescreve o conteúdo. Use sempre um arquivo dentro de um diretório.

sudo apt install fio
cd /var/tmp

O teste que mais importa para banco de dados e aplicação web é o de operações pequenas e aleatórias:

fio --name=aleatorio --filename=teste.fio --size=2G \
    --rw=randrw --rwmixread=70 --bs=4k \
    --ioengine=libaio --iodepth=32 --direct=1 \
    --runtime=60 --time_based --group_reporting

E o de escrita sequencial, que representa backup e cópia de arquivo grande:

fio --name=sequencial --filename=teste.fio --size=2G \
    --rw=write --bs=1M --ioengine=libaio --iodepth=8 \
    --direct=1 --runtime=30 --time_based --group_reporting

rm -f teste.fio

O --direct=1 ignora o cache do sistema operacional, medindo o disco de fato. Sem ele, você mede a memória e obtém números irrealisticamente bons.

Na saída, olhe IOPS e a latência clat — especialmente os percentis. Uma média boa com percentil 99 péssimo indica variação, que é o que a aplicação sente como travada intermitente.

Rode o teste em horário de baixa e por tempo suficiente. Teste de dez segundos pega apenas o cache; testes curtos são a principal razão de resultados que não se reproduzem.

As causas mais comuns, em ordem

Antes de concluir que o problema é o armazenamento, verifique:

  • Falta de índice no banco. Uma consulta varrendo milhões de linhas gera leitura massiva. É a causa número um, com folga.
  • Log em modo de depuração. Aplicação registrando cada requisição com corpo completo escreve muito mais do que parece.
  • Backup no horário errado. Dump concorrendo com o pico do dia.
  • Swap ativo. Se a memória acabou, o disco vira memória e tudo fica lento. O artigo sobre OOM killer e swap mostra como distinguir swap saudável de *thrashing*.
  • Disco quase cheio. Sistema de arquivos acima de 90% degrada por fragmentação e por falta de espaço contíguo.
  • Sistema de arquivos com opção ruim. atime atualiza a data de acesso a cada leitura, gerando escrita desnecessária.

Este último tem correção barata:

# /etc/fstab — acrescente noatime nas opções
UUID=xxxx / ext4 defaults,noatime 0 1
sudo mount -o remount /

Em servidor com muita leitura de arquivo pequeno, noatime reduz escrita de forma perceptível e não quebra nada em uso comum.

Reduzir a pressão sem trocar de disco

Antes de aumentar recurso, algumas medidas costumam resolver:

  • Mover arquivo grande para bucket. Mídia e dump em Object Storage saem do disco da VM, que é o armazenamento mais caro e mais disputado que você tem.
  • Ajustar o log. Nível adequado em produção, e retenção definida como no artigo sobre disco cheio.
  • Escalonar as tarefas pesadas. Backup, reindexação e relatório em horários diferentes, não todos às 3h.
  • Dar mais memória ao cache. Boa parte da leitura desaparece quando o conjunto quente cabe na RAM — e shared_buffers bem dimensionado economiza mais IO que qualquer ajuste de disco.

Quando o problema é mesmo do outro lado

Se depois de tudo isso os números continuarem ruins, aí sim vale abrir chamado. E vale chegar com evidência, o que torna a conversa objetiva:

  • A saída do fio com os parâmetros usados.
  • iostat -xz durante o período problemático.
  • O horário exato e a duração das ocorrências.
  • A confirmação de que a carga da máquina estava normal no momento.

Vale conferir também o *steal time* no mesmo período — se a CPU estava sendo disputada, a lentidão pode ter outra origem, como descrito no artigo sobre load average. Descartar essa hipótese antes evita um chamado sobre disco quando o assunto era outro.

Um baseline vale mais que um limiar

A conclusão que se repete em quase todo diagnóstico de disco é que faltava comparação. "Está lento" só significa alguma coisa em relação a um estado anterior conhecido.

Vale rodar o fio uma vez em cada máquina nova, com os mesmos parâmetros, e guardar o resultado junto da documentação do servidor. Leva cinco minutos e cria a referência que responde à pergunta certa depois: o disco piorou, ou a carga aumentou?

Sem esse número, toda investigação começa do zero e termina em suposição. Com ele, a comparação é imediata — e nas duas direções: se o desempenho bruto está igual ao do primeiro dia, o problema é a aplicação, e nenhuma troca de armazenamento vai resolver.

Vale também registrar o await médio em operação normal. É a métrica que muda de forma mais visível quando algo se degrada, e a que dá o alerta mais cedo.

Escrita que dá para eliminar

Vale fechar com o ângulo mais produtivo: a operação de disco mais rápida é a que não acontece.

Antes de medir capacidade ou pedir armazenamento melhor, três reduções costumam ter efeito imediato e não custam nada além de configuração.

A primeira é o log de acesso do servidor web em conteúdo estático. Cada imagem servida gera uma linha, e um site com trinta recursos por página multiplica isso por visita. Desligar o registro só nos estáticos, como mostrado no artigo sobre nginx, corta um volume expressivo sem perder informação relevante.

A segunda é a sincronização síncrona onde ela não é necessária. Banco de desenvolvimento e cache não precisam garantir gravação em disco a cada operação. Em produção, a decisão é outra — mas vale saber que ela existe e quanto custa.

A terceira é o dado que não deveria estar em disco local. Arquivo enviado por usuário, dump e histórico em bucket saem da disputa por IO da máquina, além de ficarem mais baratos e sobreviverem a ela.

Depois dessas três, o que sobra é carga legítima — e aí a conversa sobre capacidade passa a fazer sentido.

Suba um servidor em minutos

Preço em real, suporte em português e dados no Brasil.

Criar conta