Pular para conteúdo

Percentis e Latência de Cauda

O painel da BoxOffice diz que o endpoint do mapa de lugares tem latência média de 55 ms. Todos estão contentes — menos os fãs, que reclamam nas redes sociais que o mapa de lugares "demora uma eternidade" durante as aberturas de vendas. Os dois lados têm razão. A média é 55 ms; mas uma requisição em cada cem leva mais de meio segundo, e um fã que abre o mapa de lugares, atualiza, amplia dois setores e tenta três reservas faz requisições suficientes para cair numa dessas lentas quase toda visita. Esta página trata das requisições lentas: como medi-las, por que importam mais do que a média sugere e por que ficam piores à medida que os sistemas ficam mais distribuídos.


Por que médias mentem

Distribuições de latência não são curvas de sino. A maioria das requisições é rápida e parecida; uma minoria — que pega um cache miss, uma pausa de coleta de lixo, um lock, uma nova tentativa, uma conexão fria — é muito mais lenta. O resultado é uma cauda longa à direita. O gráfico mostra uma distribuição sintética, mas realista, para 100.000 requisições: 97% são "normais" (em torno de 40 ms) e 3% pegam um caminho lento (em torno de 400 ms):

2026-11-01T21:55:03.968945 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/

A média (cerca de 55 ms) não descreve nenhuma requisição real: ela é puxada para cima pela cauda e, ainda assim, esconde quão ruim a cauda é. A mediana diz o que uma requisição típica experimenta. O p99 e o p99.9 dizem o que as requisições azaradas experimentam — e são cerca de 12× e 28× mais lentas que a mediana (≈ 510 ms e ≈ 1,1 s contra ≈ 40 ms). Um painel que mostra só a média jamais revelaria isso.


Percentis

O p-ésimo percentil (escrito pXX) de um conjunto de latências é o valor abaixo do qual ficam XX% delas1:

Percentil Significado Também chamado O que diz
p50 metade das requisições é mais rápida mediana a experiência típica
p90, p95 90% / 95% são mais rápidas — a experiência "meio lenta" comum
p99 99% são mais rápidas; 1 em 100 é mais lenta cauda de "dois noves" a experiência dos azarados — e dos usuários intensos
p99.9 1 em 1.000 é mais lenta cauda de "três noves" rara, mas real; domina quando as requisições fazem fan-out
max a mais lenta observada p100 casos extremos; ruidoso, mas vale uma olhada

Para calcular um percentil exatamente, ordene os valores e pegue o da posição ⌈p × n⌉ (o método do posto mais próximo): com 1.000 requisições, o p99 é a 990ª mais rápida. Em produção, os sistemas não guardam cada latência; eles registram histogramas — contagens de requisições por faixa de latência (0–5 ms, 5–10 ms, …) — e estimam os percentis a partir das faixas. Histogramas do Prometheus, o HdrHistogram e os histogramas exponenciais do OpenTelemetry funcionam assim (aula 9).

Não se tira média de percentis

Se o servidor A tem p99 = 100 ms e o servidor B tem p99 = 1.000 ms, o p99 do conjunto não é 550 ms; ele depende de quantas requisições cada um atendeu e da forma das duas distribuições. O mesmo vale ao longo do tempo: a média de 60 valores de p99 por minuto não é o p99 da hora. Para agregar, some os histogramas (some as contagens das faixas) e calcule o percentil a partir do histograma combinado. Painéis que tiram média de percentis estão entre os erros mais comuns — e mais enganosos — de monitoramento.

Por que a cauda importa mais do que "1%" sugere

  1. Usuários fazem muitas requisições. Uma visita de um fã durante uma abertura de vendas (página do evento, mapa de lugares, atualizações, reservas, pagamento, confirmação) envolve facilmente 50 requisições. A probabilidade de que pelo menos uma seja mais lenta que o p99 é \(1 - 0.99^{50} \approx 39\%\). A "cauda de 1%" é sentida por quatro em cada dez visitas.
  2. Os usuários mais lentos costumam ser os melhores clientes. Fãs com muitos pedidos, organizadores com eventos grandes, contas com muitos dados — suas requisições fazem mais trabalho e caem na cauda. A Amazon ficou famosa por definir suas metas internas de latência no p99.9 por esse motivo2.
  3. A cauda é onde a sobrecarga começa. Como mostram as curvas de filas, o p99 sobe muito antes da média quando um componente se aproxima da saturação. É o aviso mais precoce que você recebe.

Amplificação da cauda sob fan-out

Backends modernos raramente respondem a uma requisição com uma única chamada. A página do mapa de lugares da BoxOffice chama inventory (disponibilidade), catalog (layout do local e preços), order (os ingressos que o fã já tem) e um serviço de recomendações, em paralelo; o serviço de busca consulta 20 shards de índice em paralelo e combina os resultados. Uma requisição que faz fan-out para N backends em paralelo é tão lenta quanto o mais lento deles.

Se cada chamada a um backend é lenta (acima do seu p99) com probabilidade q = 1%, de forma independente, a probabilidade de que a requisição do usuário espere por pelo menos uma chamada lenta é:

\[ P(\text{requisição do usuário lenta}) = 1 - (1 - q)^N \]
Fan-out N q = 1% (p99 do backend) q = 0,1% (p99.9 do backend)
1 1% 0,1%
5 4,9% 0,5%
20 18% 2%
100 63% 9,5%
500 99% 39%
2026-11-01T21:55:04.166496 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/

Essa é a observação central de The Tail at Scale, de Dean e Barroso3: com fan-out de 100, um backend cujo p99 é 1 segundo faz 63% das requisições dos usuários levarem mais de 1 segundo. Num grande sistema distribuído, a cauda do backend vira a mediana do usuário. Quanto mais você divide um sistema em pedaços paralelos (shards, microsserviços), mais a lentidão rara de cada pedaço passa a dominar.

BoxOffice — busca em 20 shards

O serviço de busca divide o índice de eventos em 20 shards para manter cada um pequeno e rápido. Cada shard responde em 15 ms no p50 e 120 ms no p99 (um merge ou um cache miss de vez em quando). A busca do usuário espera pelos 20:

  • P(pelo menos um shard acima de 120 ms) = 1 − 0,99²⁰ ≈ 18% — então o p82 visto pelo usuário já é ≥ 120 ms, e o p99 visto pelo usuário é determinado pelo p99.9 dos shards, ou pior.
  • Reduzir os shards pela metade, para 10, corta essa probabilidade para ~10%, mas deixa cada shard maior e mais lento. Manter 20 shards e usar hedging (abaixo) costuma ser a troca melhor.

De onde vem a cauda

A latência de cauda raramente é causada pelo caminho "normal" do código. Causas típicas3:

Fonte Exemplo Mitigação
Filas uma rajada chega enquanto um servidor está ocupado folga (utilização menor), balanceamento pelo menos carregado
Recursos compartilhados vizinhos barulhentos no mesmo host, disco ou rede isolamento, capacidade dedicada para caminhos críticos
Coleta de lixo uma JVM pausa por 200 ms ajuste do GC, coletores de pausa curta, heaps menores
Trabalho em segundo plano compactação do banco, rotação de logs, backups agendar fora do pico, limitar a taxa das tarefas em segundo plano
Cache misses cache frio depois de um deploy; chaves raras pré-aquecimento, cache maior, coalescência de requisições
Novas tentativas e timeouts a primeira tentativa estoura o timeout em 1 s, a nova tentativa funciona timeouts bem ajustados, hedging em vez de esperar
Partidas a frio um contêiner ou função serverless novo inicializa pools aquecidos, concorrência provisionada
Dependências lentas o provedor de pagamentos tem um minuto ruim timeouts, circuit breakers, processamento assíncrono

Domando a cauda

Você não consegue eliminar a variabilidade; consegue projetar para que ela não chegue ao usuário.

  • Hedged requests: envie a requisição a uma réplica; se ela não responder dentro de, digamos, a latência p95, envie uma segunda cópia a outra réplica e use a que responder primeiro. Só ~5% das requisições são duplicadas, mas a cauda encolhe drasticamente. Dean e Barroso relatam um caso do Google em que fazer hedging após 10 ms reduziu o p99.9 de uma leitura de 1.000 chaves de 1.800 ms para 74 ms, com apenas 2% mais requisições3. Faça hedging só de leituras idempotentes.
  • Tied requests: envie para duas réplicas ao mesmo tempo, cada uma sabendo da outra; a primeira a começar o processamento cancela a outra. Remove a variabilidade de fila ao custo de um pouco de coordenação.
  • Reduza o fan-out: menos partições, e maiores, para caminhos críticos em latência; pré-calcule agregados (um resumo materializado do mapa de lugares em vez de chamar cinco serviços).
  • Degrade com elegância: dê a cada backend um prazo e devolva uma resposta parcial — o mapa de lugares sem o painel de recomendações ainda é um mapa de lugares.
  • Tire trabalho do caminho crítico: o que não precisa terminar antes da resposta (e-mails, analytics, indexação de busca) vai para uma fila.
  • Mantenha a utilização baixa nos componentes críticos em latência e isole-os do trabalho em lote.

Orçamentos de latência

Uma meta voltada ao usuário precisa ser dividida entre os componentes do caminho. Para o checkout da BoxOffice (p99 < 1,5 s):

Etapa Orçamento (p99) Observações
Borda (CDN, WAF, gateway) 50 ms terminação TLS perto do usuário
Lógica do serviço order 50 ms validação, checagem de idempotência
inventory (confirmar reserva) 100 ms uma transação no primário
payment → provedor 1.000 ms o provedor domina; timeout em 1,2 s
Gravar pedido + outbox 50 ms uma transação
Margem 250 ms variação da rede, novas tentativas
Total 1.500 ms —

Somar p99s é pessimista (raramente todos acontecem na mesma requisição), e é exatamente o que você quer num orçamento. Quando uma etapa estoura seu orçamento, você sabe de quem é o problema. Cada orçamento também define o timeout daquele componente — um timeout maior que o orçamento inteiro de quem chama não protege nada.


Medindo percentis com honestidade

Duas armadilhas fazem a latência de cauda parecer melhor do que é:

  • Omissão coordenada (coordinated omission): um gerador de carga que só envia a próxima requisição depois que a anterior volta para de enviar quando o servidor trava — então ele nunca registra as requisições que teriam chegado durante a trava, e a cauda some dos resultados. Use ferramentas que mantêm uma taxa de chegada constante (modelo aberto: o constant-arrival-rate do k6, wrk2) e compare os horários de envio pretendidos e reais4. A aula 15 aprofunda o tema.
  • Medir no lugar errado: o próprio cronômetro do servidor não vê o tempo na fila de aceitação, no load balancer e na rede. Meça onde o usuário está (no cliente, ou na borda) e compare com os números do lado do servidor.

No código do BoxOffice: percentis no teste de carga (lab 2)

O teste de carga do Hands-on 2 evita as duas armadilhas. Ele usa o executor constant-arrival-rate do k6 (um modelo aberto: novas requisições continuam chegando a RATE por segundo, por mais lento que o serviço fique), mede no cliente e escreve seus critérios de aprovação como percentis por endpoint, porque uma página de detalhes rápida e uma listagem por cidade lenta, juntas numa média, não descrevem nenhuma das duas. A execução de exemplo do laboratório mostra por que a cauda é o número a observar: em 400 req/s a mediana foi 1,5 ms enquanto o p99 foi 224 ms — a média teria escondido que um fã em cada cem esperou 150 vezes mais que o típico.

load/load.js (open model, percentile thresholds)
export const options = {
  scenarios: {
    open: {
      executor: 'constant-arrival-rate',
      rate: RATE,
      timeUnit: '1s',
      duration: DURATION,
      preAllocatedVUs: Math.max(20, RATE),   // VUs ready to start requests
      maxVUs: Math.max(200, RATE * 10),      // k6 may add more if requests get slow
    },
  },
  summaryTrendStats: ['avg', 'med', 'p(95)', 'p(99)', 'max'],
  thresholds: {
    http_req_failed: ['rate<0.01'],
    'http_req_duration{name:detail}': ['p(99)<500'],
    'http_req_duration{name:list}': ['p(99)<1000'],
  },
};

Experimente: no cenário scale-out, rode o perfil load e anote o p50 e o p99. Depois use chaos em low para deixar uma réplica lenta de vez em quando. Qual percentil se move primeiro? Quanto o p99 se move em comparação com o p50?

Pense antes: SLO para o mapa de lugares

A BoxOffice quer um SLO para o endpoint do mapa de lugares. Opção A: "latência média < 100 ms". Opção B: "99% das requisições < 300 ms, medido no gateway ao longo de 28 dias". Qual, e por quê?

Resposta: B. Ela é expressa como percentil (restringe a cauda, que os usuários sentem), tem um limiar que importa para os usuários, um ponto de medição (o gateway, mais perto do usuário que o serviço) e uma janela. A opção A pode ser cumprida enquanto 3% das requisições levam mais de um segundo — exatamente a reclamação nas redes sociais. SLOs são desenvolvidos na aula 9.

Na entrevista

Sempre expresse metas de latência como percentis ("p99 < 200 ms para leituras") e, quando o seu design fizer fan-out (busca particionada, uma página que agrega vários serviços, scatter-gather), mencione a amplificação da cauda e uma mitigação: "com 20 shards, o shard mais lento domina, então eu acrescentaria hedged requests depois do p95 e devolveria resultados parciais depois de um prazo." Isso mostra que você entende como sistemas distribuídos se comportam, não só como são desenhados.

Boas práticas

  • Exporte a latência como histograma com limites de faixa nos thresholds do SLO — com uma borda de faixa em 300 ms, "p99 < 300 ms" é calculado com exatidão em vez de interpolado.
  • Rotule a latência por rota, nunca um número por serviço — uma página de detalhes rápida e uma listagem lenta precisam de percentis separados. Na BoxOffice: os rótulos name:detail e name:list no load/load.js do lab 2 (quadro acima).
  • Informe p50, p99 e p99,9 junto com o número de requisições — um p99 sobre 200 requisições é a segunda mais lenta; ele precisa de milhares para significar algo.
  • Derive todo timeout de um orçamento de latência, e propague o prazo restante — uma chamada recebe no máximo sua parte do orçamento de quem chama, para que quem chama nunca estoure antes e repita um trabalho ainda em andamento.
  • Faça hedging só de leituras idempotentes, depois do p95, com um teto de carga extra (alguns por cento) — o hedging compra latência de cauda com carga, e a carga precisa ficar limitada.

Armadilhas comuns

  • Reportar médias de latência — ou SLOs sobre médias.
  • Tirar média de percentis entre servidores ou janelas de tempo em vez de somar histogramas.
  • Ignorar o fan-out: o p99 do backend não é o p99 do usuário quando uma requisição faz muitas chamadas.
  • Fazer hedging de requisições não idempotentes: duplicar um POST /orders duplica o pedido.
  • Omissão coordenada nos testes de carga: a cauda que você não mede é a cauda que você não corrige.

Como se conecta

flowchart LR
    DIST["latência de cauda longa"] --> PCT["p50, p99, p99.9<br/>a partir de histogramas"]
    PCT --> SLO["SLOs de latência<br/>→ Confiabilidade"]
    PCT --> OBS["combinar histogramas,<br/>nunca tirar média → Observabilidade"]
    FAN["fan-out N<br/>1 − (1 − q)ᴺ"] --> AMP["a cauda do backend vira<br/>o caso comum (N grande)"]
    DIST --> SRC["fontes: GC, cache misses,<br/>cold starts, dependências lentas"]
    AMP --> TAME["hedging, resultados parciais,<br/>menos fan-out, folga"]
    SRC --> TAME
    TAME --> BUD["orçamentos de latência<br/>e timeouts → Confiabilidade"]
    PCT --> CO["medir no cliente,<br/>omissão coordenada → Performance"]

Componentes da paleta: search (20 shards: a cauda do fan-out) · app (uma página que agrega vários serviços; hedging para outra réplica) · cache (misses são uma fonte de cauda) · serverless (cold starts) · apigw (onde o SLO é medido) · payment (o provedor domina o orçamento do checkout) · metrics (histogramas, nunca média de percentis). Cenários: scale-out (com chaos), search-autocomplete. Continua em: Matemática da Disponibilidade, Observabilidade — SLOs, Confiabilidade — timeouts, Observabilidade — sinais, Performance — testes de carga, Hands-on 2.

Pontos-chave

  • Distribuições de latência têm caudas longas; a média as esconde — use p50, p99, p99.9.
  • Percentis vêm de histogramas; agregue somando histogramas, nunca tirando média de percentis.
  • Usuários e clientes intensos fazem muitas requisições, então a "cauda de 1%" atinge uma grande parte das visitas.
  • Sob fan-out N, P(lenta) = 1 − (1 − q)ᴺ: com 20 chamadas o p99 do backend atinge 18 % das requisições do usuário; a partir de ~70 chamadas, vira a mediana do usuário.
  • Combata a cauda com hedging, resultados parciais, menos fan-out, folga e trabalho assíncrono; divida metas ponta a ponta em orçamentos de latência com timeouts correspondentes.


  1. KLEPPMANN, M.; RICCOMINI, C. Designing Data-Intensive Applications, 2nd ed. O'Reilly. Chapter 2 (describing performance: response time percentiles and tail latency amplification). ↩

  2. DeCANDIA, G. et al. Dynamo: Amazon's Highly Available Key-value Store. SOSP, 2007 (service-level agreements expressed at the 99.9th percentile). ↩

  3. DEAN, J.; BARROSO, L. A. The Tail at Scale. Communications of the ACM, 56(2), 2013, pp. 74–80. ↩↩↩

  4. TENE, G. How NOT to Measure Latency. Talk, Strange Loop, 2015. ↩