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.

Nota: Este artigo assume conhecimento dos conceitos de processos (Dia 5) e systemd (Dia 7). O Dia 25 cobriu monitorização com Prometheus e Grafana para observabilidade contínua — aqui o foco é diagnóstico pontual e tuning manual.

Neste artigo

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
⚠ Atenção: Ver 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
Interpretação: Um 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...
✓ Dica: Os flame graphs são a forma mais eficaz de visualizar perf data. Cada rectângulo representa uma função no stack trace; a largura indica quanto tempo foi gasto nessa função (ou nas suas chamadas). Rectângulos largos no topo são os pontos quentes. A ferramenta 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:

  1. Load average: uptime — se load > nº de cores, há saturação algures;
  2. CPU: top ou mpstat -P ALL 1 — verificar us, sy, wa, id; se wa > 20%, saltar para o passo 4 (disco);
  3. Memória: free -h e vmstat 1 5 — verificar available, si/so; se swap activo, identificar processo com ps aux --sort=-%mem;
  4. Disco: iostat -x 1 3 — verificar %util e await; se %util > 80% e await > 20 ms, usar iotop -o para identificar processo responsável;
  5. Rede: ss -s e ss -tn — verificar Recv-Q, número de ligações estabelecidas, retransmissões; iftop -i eth0 para tráfego por ligação;
  6. Profiling: se os passos 2–5 não revelam a causa, usar perf record -a -g -- sleep 30 seguido de perf report para identificar funções com maior sobrecarga;
  7. Comparar com linha de base: registar métricas em período normal (sar -o baseline 60 1440 durante 24h) para ter uma referência contra a qual comparar períodos de degradação;
  8. 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