...

TCP Small Queues: Reduzir de forma seletiva a latência na rede do Linux

O TCP Small Queues limita, por fluxo TCP, os bytes pendentes na via de transmissão do Linux, reduzindo assim Latência incluindo o «bufferbloat», de forma específica. Vou mostrar como este mecanismo funciona no redes Linux O Stack mostra como definir limites adequados e quais as interações que surgem com o Pacing, os QDiscs e o Controlo de Congestão.

Pontos centrais

  • Limite de caudal: O TSQ limita o número de bytes pendentes por socket TCP.
  • Menos «bufferbloat»: Filas mais curtas reduzem o RTT.
  • Contrapressão: As aplicações funcionam mais lentamente quando o limite é atingido.
  • Equidade: Nenhum fluxo isolado ocupa filas inteiras.
  • Adaptativo Controlo: O limite depende da taxa e do tamanho do segmento.

Como funciona o TCP Small Queues

O TSQ entra em ação no ponto em que os segmentos TCP são QDisc e os controladores. Quando escrevo dados num socket, o kernel verifica, antes de cada enqueue, os bytes já alocados para esse fluxo. Se o fluxo atingir o limite, a lógica marca o socket como limitado e interrompe novas enqueues. Só quando a placa de rede libertar o buffer é que o socket pode voltar a enviar e eu posso novamente inserir dados na pilha. Esta restrição rigorosa mantém a Filas de espera é curto e torna os tempos de resposta mais previsíveis.

Por que é que as longas filas aumentam o tempo de resposta

Criar filas de controladores e QDisc de grande dimensão Bufferbloat, sobretudo com TSO/GSO e grandes volumes de tráfego de saída. Um download pesado pode encher as filas de saída, enquanto fluxos interativos como SSH, chamadas de API ou VoIP ficam em segundo plano. A fila sobrecarregada passa então a dominar a RTT em vez do tempo real da ligação. O controlo de congestionamento reage com lentidão, porque os acks chegam tarde, e toma decisões menos acertadas relativamente ao cwnd. O TSQ limita os bytes pré-armazenados por fluxo, para que os pacotes pequenos e urgentes cheguem rapidamente à linha.

Um olhar por baixo do capô: o que o kernel faz contar

Por baixo da superfície, o kernel não conta „pacotes“, mas sim bytes; mais precisamente: os bytes de memória já colocados na fila pelo socket. O que é determinante é o que a pilha contém em termos de estruturas skbuff, incluindo truesize que tenha sido atribuída e que ainda não tenha sido processada pela NIC. O TSQ associa a isso um Acelerar/Desacelerar- Percurso: Se um socket atingir o limite de crédito, a pilha define um sinalizador de limitação e só volta a chamar após a conclusão da transmissão (NAPI/IRQ) write_space() para que a aplicação possa voltar a enviar. Este feedback é mais rápido do que os sinais baseados exclusivamente em perdas do controlo de congestionamento e atua antes do QDisc. Com o TSO/GSO, o mecanismo mantém-se eficaz, porque o limite no antes de baseia-se no orçamento de bytes subjacente à segmentação: os super-frames de grande dimensão só são admitidos no QDisc se houver crédito suficiente disponível, o que permite limitar os picos de tráfego.

Limites dinâmicos e ritmo

Beneficio do TSQ porque o limite não permanece estático, mas sim Taxa e tem em conta o tamanho do segmento. O objetivo é, por exemplo, cerca de um milésimo de segundo de dados no caminho de transmissão por fluxo, independentemente de a velocidade ser de 100 Mbit, 1 Gbit ou 10 Gbit. Numa ligação rápida, o crédito de bytes permitido aumenta; numa ligação mais lenta, diminui. Em combinação com o TCP-Pacing, os picos de tráfego permanecem pequenos e as confirmações (Acks) são recebidas mais rapidamente. Desta forma, consigo uma redução sensível Picos de latência, sem reduzir desnecessariamente o débito.

Interação por socket e entre aplicações

O TSQ só produz o seu efeito se a aplicação também sentir a contrapressão. Por isso, tenho em conta definições como SO_SNDBUF, TCP_NOTSENT_LOWAT e o autocorking. Uma janela de buffer de envio demasiado grande pode empurrar muitos bytes para a pilha num curto espaço de tempo; o TSQ abranda o processo, mas a aplicação só se apercebe disso quando send() fica bloqueado ou devolve EAGAIN. Com TCP_NOTSENT_LOWAT ajusto a parte „não enviada“ no Userland e, assim, complemento o TSQ no lado do kernel. Autocorking (ou explicitamente TCP_CORK/MSG_MORE) ajuda a agrupar pequenas gravações sem provocar picos de latência. Limites de ritmo por socket (por exemplo, através de SO_MAX_PACING_RATE) estão em sintonia com o TSQ: a taxa suaviza a variação temporal, enquanto o limite de bytes restringe a variação espacial. Importante: TCP_NODELAY Desativa o Nagle e pode aumentar a interatividade, mas sem o TSQ aumenta o risco de picos de tráfego; com o TSQ, tenho ambos sob controlo.

Guia prático: valores TSQ relevantes

Estabeleço o contexto global com net.ipv4.tcp_limit_output_bytes (Sysctl). Os valores predefinidos habituais situam-se entre 128 e 262 KB por fluxo. Para muitas cargas de trabalho da Web e de API, opto por valores mais baixos, para que as respostas interativas se mantenham rápidas. Para cópias de segurança ou replicação, aumentei moderadamente o limite, desde que o RTT se mantenha estável. Quem quiser aprofundar os conhecimentos sobre a página de filas encontrará noções básicas sobre Filas de pacotes no servidor, que ajudam na classificação.

Cenário Taxa de ligação Valor de referência tcp_limit_output_bytes Objetivo
API/HTTP muito interativa 100 Mbit – 1 Gbit 64–128 KB baixa RTT, pontas curtas
Carga mista: Web + downloads 1–10 Gbit 128–256 KB Saldo de Rendimento e latência
Replicação/Cópias de segurança 1–10 Gbit 256–512 KB fluxo em massa constante, aceitável Latência
WAN com RTT elevado 10–100 Mbit 96–192 KB ráfagas mais curtas, mais justas Tacos

QDisc e controlo de congestionamento em conjunto

O TSQ funciona na entrada do QDisc, enquanto algoritmos como o fq_codel gerem o congestionamento na linha. Em conjunto, reduzem as filas de espera e mantêm a distribuição equitativa. Com TCP BBR beneficio-me ainda mais, porque medições de RTT mais realistas conduzem a um melhor ritmo e controlo do cwnd. O CUBIC também reage de forma mais harmoniosa quando elimino tempos de enfileiramento excessivos. Assim, o débito cresce organicamente, enquanto o Tempo de resposta continua sob controlo.

Virtualização e pilhas de nuvem

Nas máquinas virtuais, acumulam-se vários níveis de buffer: QDisc do convidado, filas virtio/vhost, QDisc do anfitrião e a placa de rede física. Mantenho o TSQ ativo no convidado e defino aí um limite conservador, para que não cheguem picos de tráfego intensos ao anfitrião. No hipervisor, garanto cadeias de latência curtas através de QDiscs justos, anéis TX moderados e um IRQ-pinning bem definido. O SR-IOV pode reduzir a latência, mas transfere a responsabilidade para os convidados: sem o TSQ no convidado, corre-se o risco de filas VF longas. Nos contentores, o TSQ por NetNS como de costume; através do cgroup-pacing e dos limites da CPU, evito que um vizinho ruidoso aumente indiretamente a latência. É também importante prestar atenção ao coalescing e aos offloads no caminho do virtio: um agrupamento excessivo prolonga os acks, enquanto um agrupamento insuficiente reduz a eficiência – faço os ajustes de acordo com a meta de latência, sem ser dogmático.

Wi-Fi e sistemas incorporados: como lidar corretamente com casos especiais

Nas ligações Wi-Fi, o que importa é a Agregação na camada MAC. Se eu definir um número insuficiente de bytes no caminho de transmissão, o controlador não consegue agrupar tantos quadros, o que reduz a eficiência. Nessas configurações, aumentei o limite com cuidado e verifiquei o grau de agregação. As plataformas OpenWrt e incorporadas beneficiam também de percursos simplificados nos controladores e de operações atómicas mais reduzidas. Testo cada ajuste sob carga de rádio real antes de um Perfil desenrolar amplamente.

Monitorização e métricas que realmente contam

Eu observo o RTTAnálise da distribuição por socket e observação de valores atípicos, não apenas dos valores médios. Com os comandos «ss», «tc» e os exportadores, leio os comprimentos das filas, as retransmissões e a «pacing_rate». Os programas eBPF fornecem-me eventos quando os sockets são limitados e voltam a ficar livres. O «Time-to-First-Byte» e os percentis 95.º e 99.º mostram se o TSQ está a surtir efeito. Sem valores de medição, qualquer Otimização um voo às cegas.

Testes A/B e de carga com resultados significativos

Medei os efeitos do TSQ de forma reproduzível: primeiro a linha de base sem alterações, depois varreduras isoladas de parâmetros (por exemplo, 64, 96, 128, 192 KB). Para cargas de trabalho mistas, executo fluxos paralelos (em massa + muitas solicitações curtas) e comparo os percentis 95 e 99 das latências, não apenas a mediana. Mesmo quando interrompo claramente as execuções de teste (aquecimento, janela de medição, arrefecimento), os artefactos continuam a ser percetíveis. Presto atenção às constantes: padrões de carga útil iguais, rota/MTU idênticas, frequências de CPU do servidor e do cliente idênticas. Em ligações WAN, simulo atrasos/jitter/perdas com tc netem, para verificar se os limites do TSQ não atingem o teto demasiado cedo quando o BDP é elevado. Só quando os percentis se tornarem mais estreitos e as retransmissões/perdas se mantiverem estáveis é que incorporo os valores na produção.

Otimização de hardware e detalhes dos controladores

Vou verificar as definições TSO/GSO, o buffer circular da placa de rede e o controlo de IRQ, para que TSQ funciona bem. Anéis TX demasiado grandes prolongam a fila de espera no dispositivo; anéis demasiado pequenos reduzem a utilização do sistema. Um agrupamento grosseiro de interrupções atrasa as respostas de confirmação (Acks), enquanto um agrupamento preciso aumenta a carga da CPU. Adapto a moderação de forma prática e, para começar, remeto para Coalescência de interrupções. O objetivo continua a ser uma Latência com um débito sustentável.

NUMA, RSS e afinidade da CPU

Filas curtas servem de pouco se os pacotes estiverem constantemente a atravessar os limites NUMA. Associo as filas RX/TX, através do RSS/irqbalance, aos núcleos do mesmo domínio NUMA em que a aplicação está a ser executada. Com o XPS/RPS, controlo quais as CPUs que assumem o trabalho de transmissão (TX), evitando assim saltos entre sockets. Menos falhas de cache e menos contenção de bloqueios ajudam indiretamente o TSQ: As confirmações de conclusão chegam mais rapidamente, o socket é „desengarrafado“ mais cedo e não se verificam picos de latência. Quando há um grande número de fluxos por anfitrião, planeio filas suficientes e evito que vários fluxos intensos colidam no mesmo anel TX.

Passo a passo: verificar se o TSQ está ativo

Começo por olhar para Sysctl: O comando `sysctl net.ipv4.tcp_limit_output_bytes` mostra o limite atual. Em seguida, utilizo o `ss -tin` para analisar sockets individuais, prestando atenção aos valores de `send-q` e `rtt`, e comparo fases de carga com e sem ajuste do limite. Com o iperf3, gerar carga em segundo plano e medir, em paralelo, os tempos de resposta da API, para tornar visíveis as prioridades. O comando tc -s qdisc fornece-me os números de pacotes e de pacotes descartados da disciplina de saída. Se os percentis 95 e 99 permanecerem próximos e o CPU‑Carga na estrutura, a escolha do limite é adequada.

Erros comuns e anti-padrões

  • „Mais buffer = mais desempenho“: isto é verdade para testes de débito sem um objetivo de latência, mas deixa de se verificar no caso de serviços interativos. O TSQ substitui filas sobredimensionadas por um crédito adaptado às necessidades de cada fluxo.
  • „O TSQ prejudica o rendimento“: Quando configurado corretamente, o TSQ limita os picos de tráfego, não a taxa média. Em cargas de trabalho em massa, aumentei moderadamente o limite e medi os percentis, em vez de me limitar apenas ao pico em Mbit/s.
  • „O pacing por si só é suficiente“: o alisamento temporal é importante, mas, sem um limite de bytes, os quadros GSO de grande dimensão acabam por entrar na QDisc na mesma. O TSQ e o pacing complementam-se.
  • „Um valor para todos“: as cargas de trabalho, as ligações e as placas de rede diferem entre si. Trabalho com intervalos de valores e faço a validação por ambiente.
  • „Apenas o TCP é afetado“: o foco está no TCP, mas existem outros parâmetros de ajuste no sistema (por exemplo, para a carga UDP). Impedir que protocolos paralelos obstruam as mesmas filas de forma descontrolada.

Conclusão: a latência sob controlo de forma específica

O TSQ transfere o controlo das filas de controladores para o Soquete e, assim, reduz os engarrafamentos diretamente na fonte. Limito os bytes pré-armazenados por fluxo, garantindo assim respostas rápidas, um RTT mais baixo e filas distribuídas de forma equitativa. Em combinação com o fq_codel e um controlo de congestionamento moderno, o tempo de resposta mantém-se fiável mesmo sob carga. Trato os casos especiais de Wi-Fi e sistemas incorporados com limites adaptados e testes em condições reais. Quem observa os indicadores e ajusta os limites gradualmente mantém a Latência consistentemente baixo, sem perder rendimento desnecessário.

Artigos actuais

Servidor com «TCP Small Queues» otimizadas para reduzir a latência da rede
Servidores e Máquinas Virtuais

TCP Small Queues: Reduzir de forma seletiva a latência na rede do Linux

O TCP Small Queues no kernel do Linux limita o número de pacotes TCP armazenados em buffer por fluxo e é uma ferramenta poderosa para a otimização da latência. Descubra como o TSQ reduz o «bufferbloat» e melhora os tempos de resposta dos servidores.

Centro de dados com racks de servidores e visualização estilizada de dados para a otimização do desempenho do MySQL
Bases de dados

Histogramas do MySQL – Melhores planos de consulta sem índice

Descubra como os histogramas do MySQL fornecem estatísticas precisas ao otimizador, permitem melhores planos de consulta e melhoram significativamente o seu ajuste de SQL sem a necessidade de índices adicionais.