Dia 28: Optimização de Desempenho no Linux — CPU, Memória, I/O e perf
Um servidor lento não é um problema — é um sintoma. Antes de adicionar RAM, trocar discos ou migrar para uma máquina maior, é preciso responder a uma pergunta simples: onde está o gargalo? Em sistemas Linux, o desempenho degrada-se por quatro razões fundamentais: a CPU está saturada, a memória esgotou-se, o disco é lento, ou a rede é o limite. Este artigo percorre as ferramentas que respondem a cada uma dessas perguntas — top, vmstat, iostat, ss e o perf — o profiler do kernel Linux.
Neste artigo
- Introdução — os quatro gargalos
- Monitorizar CPU com top, vmstat e mpstat
- Monitorizar memória com free e vmstat
- I/O de disco com iostat e iotop
- Rede com ss e iftop
- perf — profiling do kernel
- Erros comuns e lista de verificação
Introdução — os quatro gargalos
A análise de desempenho no Linux assenta num modelo de quatro recursos finitos: CPU (ciclos de processamento), memória (RAM e swap), disco (I/O de leitura/escrita) e rede (largura de banda e latência). Quando um servidor está lento, pelo menos um destes quatro está saturado. A tarefa do administrador é identificar qual — e agir sobre ele.
A USE Method de Brendan Gregg resume a abordagem: para cada recurso, verificar Utilização (Utilization), Saturação (Saturation) e Erros. Utilização alta não é necessariamente problema; saturação (filas a crescer, latência a subir) é o verdadeiro sinal de alarme.
| Recurso | Ferramenta principal | Sintoma de saturação |
|---|---|---|
| CPU | top, vmstat, mpstat | load average > nº de cores; run queue elevada |
| Memória | free, vmstat | si/so em vmstat; swap activo |
| Disco | iostat, iotop | %util > 80%; await > 20 ms |
| Rede | ss, iftop | retransmissões; buffer cheio |
O load average em uptime e top é o primeiro indicador rápido: três números representam a média de processos em fila de execução ( runnable + a dormir em D-state ) nos últimos 1, 5 e 15 minutos. Se o primeiro número for consistentemente superior ao número de cores da CPU, há saturação de CPU.
# Verificar load average e número de cores
uptime
# 10:23:01 up 42 days, 3:17, 3 users, load average: 4.52, 3.10, 2.80
nproc
# 4
# load 4.52 em máquina de 4 cores → saturação de CPU
Monitorizar CPU com top, vmstat e mpstat
O top é a ferramenta universal — presente em praticamente todas as distribuições. Mostra, em tempo real, os processos que mais CPU consomem, o load average e a distribuição de tempo da CPU (man top). O cabeçalho %Cpu(s) detalha o tempo gasto em:
- us — user space (aplicações normais);
- sy — kernel space (chamadas de sistema);
- id — idle (CPU parada, sem trabalho);
- wa — I/O wait (CPU à espera de disco ou rede);
- st — steal (tempo roubado pelo hypervisor, relevante em VMs cloud).
Valores elevados de wa (I/O wait) indicam que a CPU não é o problema — o disco é. O processador está parado à espera que operações de leitura/escrita terminem. Corrigir isso exige optimização de I/O, não de CPU.
# top — visão interactiva
top - 10:23:01 up 42 days, 3:17, 3 users, load average: 4.52, 3.10, 2.80
Tasks: 178 total, 2 running, 176 sleeping, 0 stopped, 0 zombie
%Cpu(s): 82.3 us, 12.1 sy, 0.0 ni, 2.1 id, 3.0 wa, 0.0 hi, 0.5 si, 0.0 st
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8421 postgres 20 0 2.5g 1.2g 120m S 98.0 6.2 1:23.45 postgres
7102 nginx 20 0 180m 45m 12m S 45.0 0.2 0:12.30 nginx
# Ordenar por CPU: prima P em top
# Ordenar por memória: prima M
# Ver threads: prima H
# Actualizar a cada 2s (por omissão) ou especificar: top -d 1
O htop é uma versão melhorada de top — com interface a cores, scroll, e possibilidade de matar processos directamente com F9. Não vem instalado por omissão em muitas distribuições, mas é um dos primeiros pacotes que qualquer administrador instala: apt install htop ou dnf install htop.
O vmstat oferece uma visão snapshot de CPU, memória e I/O numa só linha — ideal para identificar saturação rapidamente (man vmstat). A primeira linha mostra médias desde o arranque; as seguintes mostram o estado actual.
# vmstat — amostras de 1 em 1 segundo, 5 amostras
vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- --------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 124000 82000 456000 0 0 12 18 4200 1800 78 10 10 2 0
4 0 0 118000 82000 456000 0 0 8 24 8400 3200 85 8 5 2 0
3 0 0 112000 82000 456000 0 0 10 16 7600 2900 82 9 7 2 0
# Colunas críticas:
# r → processos a esperar por CPU (run queue). Se r > nº de cores → CPU saturada
# b → processos em D-state (à espera de I/O). Se b > 0 → gargalo de I/O
# us → user CPU %. sy → kernel CPU %. id → idle %. wa → I/O wait %
# cs → context switches por segundo. Valor muito alto pode indicar thrashing
A coluna r (run queue) é o indicador mais directo de saturação de CPU: representa processos prontos a executar mas sem core livre. Se r for consistentemente superior ao número de cores (nproc), a CPU é o gargalo.
O mpstat (do pacote sysstat) mostra a utilização por core, revelando se a carga está bem distribuída ou concentrada num único core — comum em aplicações single-threaded.
# Instalar sysstat (inclui iostat, mpstat, sar, pidstat)
# Debian/Ubuntu: apt install sysstat
# RHEL/Rocky: dnf install sysstat
# mpstat — utilização por core, amostra de 1s
mpstat -P ALL 1 3
# 10:23:01 AM CPU %usr %nice %sys %iowait %idle
# 10:23:02 AM all 78.2 0.0 9.1 2.0 10.7
# 10:23:02 AM 0 95.1 0.0 3.2 0.0 1.7
# 10:23:02 AM 1 61.3 0.0 15.0 4.0 19.7
# Se core 0 está a 95% e os outros a 60%, há uma aplicação
# single-threaded a saturar um único core
Monitorizar memória com free e vmstat
O free é a ferramenta mais directa para ver o estado da memória (man free). O output de free -h (formato humano) mostra total, usado, livre, partilhado, cache/buffer e disponível.
# free — formato legível, actualizar a cada 2s
free -h -s 2
total used free shared buff/cache available
Mem: 7.7Gi 3.2Gi 512Mi 120Mi 4.0Gi 4.2Gi
Swap: 2.0Gi 0.0Bi 2.0Gi
# A coluna "available" é a mais importante
# Inclui memória recuperável de cache/buffers
# NÃO confundir "free" baixo com problema — Linux usa
# memória livre para cache de disco (acelera I/O)
# Forçar libertação de cache (apenas em testes — NÃO em produção)
# echo 3 > /proc/sys/vm/drop_caches
free baixo e concluir que falta memória é o erro mais comum de iniciantes. O Linux usa memória livre para cache de disco — é comportamento normal e desejável. A coluna available é o indicador correcto: representa a memória realmente disponível para novas aplicações sem recorrer a swap.
O vmstat complementa o free ao mostrar swap activity em tempo real. As colunas si (swap in) e so (swap out) indicam páginas de memória a serem transferidas para/de swap. Valores diferentes de zero significam que o sistema está a usar swap activamente — a memória RAM esgotou-se.
# vmstat — foco em memória e swap
vmstat 1 5
procs ---memory---------- ---swap-- -----io---- -system-- --------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa
1 0 51200 64000 82000 380000 120 80 45 20 3200 1500 60 15 20 5
2 1 51400 62000 82000 380000 200 150 52 35 4100 2200 55 18 18 9
# si=200, so=150 → swap activo! A memória esgotou
# swpd a aumentar → páginas a serem movidas para swap
# b=1 → processo em D-state (à espera de I/O do swap)
# Ver os top consumidores de memória:
ps aux --sort=-%mem | head -10
Para identificar os processos responsáveis, ps aux --sort=-%mem ordena por consumo de memória. O pidstat -r 1 (do sysstat) mostra a evolução de RSS (memória residente) por processo ao longo do tempo.
O parâmetro do kernel vm.swappiness controla a tendência do sistema para usar swap. O valor por omissão é 60 (escala 0–100). Em servidores de base de dados ou máquinas com muita RAM, reduzir para 10 evita swap prematuro; em servidores de ficheiros com pouca RAM, 60 é razoável.
# Ver swappiness actual
cat /proc/sys/vm/swappiness
# 60
# Reduzir temporariamente (não sobrevive a reboot)
sysctl vm.swappiness=10
# Persistente: adicionar a /etc/sysctl.conf
echo "vm.swappiness=10" >> /etc/sysctl.conf
sysctl -p
# Em servidores de BD (PostgreSQL, MySQL): swappiness=1 ou 10
# Em desktops/workstations: 60 (por omissão)
# Em servidores de ficheiros com pouca RAM: 60 ou até 80
I/O de disco com iostat e iotop
O iostat (do pacote sysstat) é a ferramenta de referência para diagnóstico de I/O de disco (man iostat). Mostra taxas de transferência, tempos de espera e percentagem de utilização por dispositivo.
# iostat — estendido, por dispositivo, amostras de 1s
iostat -x 1 3
Linux 6.8.0-45-generic (servidor) 08/05/2026 _x86_64_ (4 CPU)
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %util await
sda 5.2 82.3 20.8 329.2 0.1 12.3 95.6 18.5
nvme0n1 120.5 245.8 482.0 983.2 2.0 30.5 72.1 2.1
# Colunas críticas:
# %util → percentagem de tempo em que o dispositivo teve I/O
# >80% sustentado → dispositivo saturado
# await → tempo médio de espera por pedido (ms)
# >20ms em SSD/NVMe → problema; >10ms em HDD rotativo é normal
# r/s, w/s → leituras/escritas por segundo
# rkB/s, wkB/s → kilobytes lidos/escritos por segundo
await elevado com %util baixo indica que o dispositivo não está saturado mas responde lentamente — pode ser problema de controlador, firmware ou uma partição em modo degradado (RAID reconstrução). Um await baixo com %util a 100% significa que o disco está genuinamente saturado — há demasiados pedidos para a largura de banda do dispositivo.
O iotop é o equivalente ao top para I/O de disco — mostra, em tempo real, os processos que mais leem e escrevem. Requer root e o kernel option CONFIG_TASK_DELAY_ACCT activo.
# iotop — apenas processos com I/O activo
sudo iotop -o
# TID PRIO USER DISK READ DISK WRITE SWAPIN IO COMMAND
# 8421 be/4 postgres 0.00 B/s 1.20 M/s 0.00 % 5.2 % postgres -D
# 7102 be/4 nginx 4.00 K/s 0.00 B/s 0.00 % 0.1 % nginx: worker
# Modo batch (para scripting/logs)
sudo iotop -b -o -n 3 > iotop.log
# Alternativa sem iotop: pidstat -d
pidstat -d 1 3
# PID kB_rd/s kB_wr/s kB_ccwr/s Command
# 8421 0.0 1200.0 0.0 postgres
Quando o I/O é o gargalo, as optimizações dependem do tipo de carga. Para base de dados: aumentar readahead, ajustar o scheduler de I/O, migrar de HDD para SSD/NVMe, ou usar RAID. Para aplicações de registos: activar logrotate com compressão e considerar journald em modo binário.
# Ver scheduler de I/O actual (por dispositivo)
cat /sys/block/sda/queue/scheduler
# [mq-deadline] kyber bfq none
# Mudar scheduler (temporário, não sobrevive a reboot)
echo "none" > /sys/block/nvme0n1/queue/scheduler
# NVMe: "none" (o dispositivo faz gestão própria)
# SATA SSD: "mq-deadline" ou "none"
# HDD rotativo: "bfq" (fairness) ou "mq-deadline"
# Ajustar readahead
blockdev --getra /dev/sda
# 256 (128 KiB por defeito)
blockdev --setra 4096 /dev/sda # 2 MiB — útil para leituras sequenciais
# Ver detalhes do dispositivo (rotação, tipo)
lsblk -d -o NAME,ROTA,TYPE,SIZE,MODEL
# NAME ROTA TYPE SIZE MODEL
# sda 1 disk 500G Samsung SSD 870
# nvme0n1 0 disk 1.0T Samsung NVMe 990
# ROTA=0 → SSD/NVMe; ROTA=1 → HDD rotativo
Rede com ss e iftop
O ss (socket statistics) é o substituto moderno do netstat (man ss). Mostra todos os sockets TCP/UDP activos, estado da ligação, portas em escuta e — com -i — estatísticas de interface em tempo real.
# ss — sockets TCP em escuta, com processo associado
ss -tlnp
# State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
# LISTEN 0 128 0.0.0.0:22 0.0.0.0:* sshd
# LISTEN 0 511 0.0.0.0:80 0.0.0.0:* nginx
# LISTEN 0 100 0.0.0.0:5432 0.0.0.0:* postgres
# Sockets estabelecidos (todas as ligações activas)
ss -tn state established
# Recv-Q Send-Q Local Address:Port Peer Address:Port
# 0 0 192.168.1.10:5432 192.168.1.20:54320
# 0 0 192.168.1.10:80 10.0.0.5:51800
# Estatísticas por interface (taxa de transferência em tempo real)
ss -i 1
# Estatísticas sumárias de rede
ss -s
# Total: 85 (kernel 92)
# TCP: 12 (estab 5, closed 2, orphaned 0, timewait 2)
# Ver ligações com Recv-Q alto (buffer cheio = leitura lenta)
ss -tn | awk '$1 > 0 {print}'
# Recv-Q alto indica que a aplicação não está a ler dados
# rápido o suficiente → possível bottleneck de processamento
O iftop mostra a largura de banda de rede por ligação em tempo real — qual IP está a gerar tráfego e para onde. Requer root e uma interface de captura (precisa de libpcap).
# iftop — tráfego por ligação na interface eth0
sudo iftop -i eth0
# 19.1Kb 38.1Kb 57.2Kb 76.3Kb 95.4Kb
# └─────────┴─────────┴─────────┴─────────┴─────────
# servidor ⇒ 10.0.0.5 2.4Kb 3.1Kb 2.8Kb
# ⇐ 1.2Kb 1.5Kb 1.3Kb
# servidor ⇒ 192.168.1.20 5.1Kb 6.2Kb 5.8Kb
# ⇐ 8.0Kb 9.1Kb 8.5Kb
# TX: cum 45.2KB peak 12.3Kb rates 7.5Kb 8.2Kb 7.9Kb
# RX: cum 38.1KB peak 10.1Kb rates 5.2Kb 6.0Kb 5.5Kb
# Alternativa sem iftop: nload (mais simples) ou ip -s link
ip -s link show eth0
# RX: bytes 45231891 packets 82134 errors 0 dropped 0
# TX: bytes 38102934 packets 71029 errors 0 dropped 0
# Ver erros de interface (descartes, colisões, frame errors)
ip -s -s link show eth0
A coluna Recv-Q do ss é um indicador subtil mas importante: se for consistentemente elevada, a aplicação não está a ler do socket rápido o suficiente. Combinado com Send-Q alto no peer, indica congestionamento de rede ou aplicação lenta.
perf — profiling do kernel
O perf é a ferramenta de profiling oficial do kernel Linux (man perf, wiki perf). Faz parte do pacote linux-tools e usa os hardware de contadores de desempenho da CPU (PMU) para recolher amostras com sobrecarga mínima — tipicamente menos de 1%. Ao contrário das ferramentas anteriores (que mostram quanto recursos são consumidos), o perf mostra onde o tempo é gasto, ao nível de função.
# Instalar perf
# Debian/Ubuntu: apt install linux-tools-generic linux-tools-common
# RHEL/Rocky: dnf install perf
# perf stat — contadores de hardware para um comando
perf stat -a sleep 5
# Performance counter stats for 'system wide':
# 12.500,00 cpu-clock (msec) # 2.500 CPUs utilized
# 1.234,56 context-switches # 0.099 M/sec
# 120,00 cpu-migrations # 0.010 M/sec
# 8.432,10 page-faults # 0.675 M/sec
# 45.234.567 cycles # 3.619 GHz
# 18.234.567 instructions # 0,40 insn per cycle
# perf top — funções que mais consomem CPU (interactivo)
sudo perf top
# Samples: 10K of event 'cycles'
# Overhead Shared Object Symbol
# 12.34% [kernel] [k] _raw_spin_lock_irqsave
# 8.12% postgres [.] hash_search
# 5.67% [kernel] [k] copy_user_enhanced_fast_string
O perf record recolhe amostras durante um período e perf report apresenta o relatório. Para além de ciclos de CPU, o perf pode profile eventos específicos como cache misses, branch misses, page faults e context switches.
# perf record — recolher amostras durante 10 segundos
sudo perf record -a -g -- sleep 10
# [ perf record: Woken up 42 times to write data ]
# [ perf record: Captured and wrote 12.345 MB perf.data ]
# perf report — analisar amostras recolhidas
sudo perf report --stdio
# # Samples: 45K
# #
# # Overhead Command Shared Object Symbol
# # ........ ......... ............. ......................
# #
# 15.23% postgres [kernel] [k] handle_mm_fault
# 12.10% postgres postgres [.] exec_simple_query
# 8.45% postgres libc-2.31.so [.] malloc
# 6.20% swapper [kernel] [k] cpu_idle
# Flame graph (requer flamegraph scripts de Brendan Gregg)
sudo perf record -F 99 -a -g -- sleep 30
sudo perf script > out.perf
git clone https://github.com/brendangregg/FlameGraph
./FlameGraph/stackcollapse-perf.pl out.perf | ./FlameGraph/flamegraph.pl > perf.svg
# Eventos disponíveis para profiling
sudo perf list | head -30
# cache-misses, branch-misses, page-faults, context-switches...
perf report em modo interactivo (sem --stdio) permite navegar com as setas e expandir nós da call stack.
Para casos em que perf não está disponível ou não se tem permissões de root, alternativas como strace (rastreia chamadas de sistema), bpftrace (eBPF — tracing moderno do kernel) ou bcc-tools oferecem capacidades de diagnóstico sem instalar cabeçalhos do kernel.
# bpftrace — one-liners de diagnóstico
# Contar syscalls por processo (10 segundos)
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); } interval:s:10 { exit(); }'
# Latência de I/O por processo
sudo bpftrace -e 'tracepoint:block:block_rq_issue { @start[arg.bytes] = nsecs; } tracepoint:block:block_rq_complete /@start[arg.bytes]/ { @usec[comm] = (nsecs - @start[arg.bytes]) / 1000; delete(@start[arg.bytes]); }'
# strace — rastrear chamadas de sistema de um processo
strace -c -p $(pgrep -f nginx | head -1) -e trace=network
# % time seconds usecs/call calls errors syscall
# 45.23 0.045 12 3800 0 accept4
# 30.10 0.030 10 3100 0 recvfrom
# 10.50 0.010 5 2000 0 sendto
Erros comuns e lista de verificação
Os erros mais frequentes em análise de desempenho não são erros de ferramenta — são erros de interpretação. Confundir sintomas com causas, optimizar o recurso errado, ou tirar conclusões de amostras demasiado curtas.
| Erro comum | Sintoma | Solução correcta |
|---|---|---|
Confundir free baixo com falta de memória |
free=512 MiB em 8 GiB total | Ver available, não free. Linux usa RAM livre para cache |
| Adicionar RAM quando swap é 0 | Aplicação lenta | Se si/so=0 não é memória. Verificar CPU (us/sy) ou I/O (wa/%util) |
| Optimizar CPU com wa alto | us=20%, wa=60% | wa alto = I/O wait. O disco é o problema, não a CPU |
| Concluir de uma amostra | vmstat 1 1 mostra r=6 | Sempre 3+ amostras. Pico único pode ser transitório |
| %util=100% conclui saturação | SSD com %util=100% mas await=2ms | %util em SSDs é enganador. Ver await e taxa de transferência real |
| Aumentar swappiness para reduzir swap | Swap activo | Reduzir swappiness (10), não aumentar. E resolver a causa: falta de RAM |
Lista de verificação para diagnóstico de desempenho — percorrer por ordem, do geral ao específico:
- Load average:
uptime— se load > nº de cores, há saturação algures; - CPU:
topoumpstat -P ALL 1— verificarus,sy,wa,id; sewa > 20%, saltar para o passo 4 (disco); - Memória:
free -hevmstat 1 5— verificaravailable,si/so; se swap activo, identificar processo comps aux --sort=-%mem; - Disco:
iostat -x 1 3— verificar%utileawait; se%util > 80%eawait > 20 ms, usariotop -opara identificar processo responsável; - Rede:
ss -sess -tn— verificarRecv-Q, número de ligações estabelecidas, retransmissões;iftop -i eth0para tráfego por ligação; - Profiling: se os passos 2–5 não revelam a causa, usar
perf record -a -g -- sleep 30seguido deperf reportpara identificar funções com maior sobrecarga; - Comparar com linha de base: registar métricas em período normal (
sar -o baseline 60 1440durante 24h) para ter uma referência contra a qual comparar períodos de degradação; - Documentar e não repetir: registar o gargalo encontrado e a optimização aplicada. Na próxima degradação, começar por verificar se é o mesmo recurso ou um novo.
As ferramentas deste artigo cobrem diagnóstico pontual — análise manual de um problema num momento específico. Para monitorização contínua e alertas automáticos, a stack Prometheus + Grafana (Dia 25) complementa estas ferramentas: node_exporter recolhe as mesmas métricas (CPU, memória, disco, rede) e armazena-as historicamente, permitindo correlacionar incidentes com tendências de longo prazo.
Artigos Relacionados
- Dia 25: Monitorização com Prometheus e Grafana — a stack de observabilidade contínua que complementa as ferramentas de diagnóstico deste artigo
- Dia 26: Registos Centralizados no Linux — correlacionar registos com métricas de desempenho para encontrar causas raiz
- Dia 5: Processos e Jobs no Linux — base de
ps,tope gestão de processos - Dia 7: Systemd no Linux — Services, Timers e journalctl — diagnóstico de serviços que afectam o desempenho
- Dia 8: Redes Linux — ip, ss, nmcli e DNS — base de
sse diagnóstico de rede