3 min

ZSTD vs Brotli vs GZIP: wybór kompresji dla API

Porównanie ZSTD, Brotli i GZIP dla API: szybkość, współczynnik kompresji, koszt CPU i praktyczne domyślne ustawienia dla JSON i binarnych payloadów w produkcji.

ZSTD vs Brotli vs GZIP: wybór kompresji dla API

Co to jest kompresja API (i kiedy się opłaca)

Kompresja odpowiedzi API oznacza, że serwer koduje ciało odpowiedzi (często JSON) do mniejszego strumienia bajtów zanim wyśle je po sieci. Klient (przeglądarka, aplikacja mobilna, SDK lub inna usługa) potem dekompresuje. Przez HTTP negocjuje się to nagłówkami takimi jak Accept-Encoding (co klient obsługuje) i Content-Encoding (co wybrał serwer).

Co to daje API

Kompresja przede wszystkim przynosi trzy korzyści:

  • Mniej transferu: mniejsze odpowiedzi zużywają mniej bajtów end‑to‑end.
  • Niższa latencja na ograniczonych łączach: mniej bajtów często oznacza szybsze pobieranie na mobilnych sieciach, zatłoczonym Wi‑Fi i w połączeniach międzyregionowych.
  • Niższe koszty egressu: jeśli płacisz za dane wychodzące, zmniejszenie transferu może obniżyć rachunek.

Koszt jest prosty: kompresja oszczędza pasmo, ale kosztuje CPU (kompresja/dekompresja) i czasem pamięć (bufory). Czy się opłaca zależy od Twojego wąskiego gardła.

Kiedy kompresja daje najwięcej

Kompresja błyszczy, gdy odpowiedzi są:

  • Bogate w tekst i powtarzalne, jak JSON, odpowiedzi GraphQL, HTML czy logi.
  • Średnie do duże, gdzie ucięcie dziesiątek lub setek kilobajtów ma znaczenie.
  • Serwowane po wolnych lub drogich sieciach, np. mobile, międzynarodowe połączenia lub ruch między regionami.

Jeśli zwracasz duże listy JSON (katalogi, wyniki wyszukiwania, analityka), kompresja często jest jednym z najprostszych usprawnień.

Kiedy daje najmniej

Kompresja zwykle źle wykorzystuje CPU, gdy odpowiedzi są:

  • Bardzo małe (np. kilkaset bajtów). Nagłówek + koszt CPU mogą przewyższyć oszczędności.
  • Już skompresowane (JPEG/PNG, MP4, ZIP, wiele PDF). Ponowna kompresja zwykle niewiele daje, a czasem zwiększa rozmiar.
  • Usługi ograniczone CPU (hot endpoints z dużym obciążeniem obliczeniowym). Dodanie kompresji może zwiększyć tail latency.

Osie decyzji używane w tym przewodniku

Wybierając między ZSTD vs Brotli vs GZIP dla kompresji API, praktyczna decyzja zwykle sprowadza się do:

  1. Redukcja rozmiaru (współczynnik kompresji)
  2. Latencja (czas serwera do pierwszego bajtu plus dekompresja po stronie klienta)
  3. Wsparcie po stronie klienta (co Twoi konsumenci i pośrednicy obsługują)

Reszta artykułu dotyczy balansowania tych trzech czynników dla Twojego API i wzorców ruchu.

ZSTD vs Brotli vs GZIP: szybkie porównanie

Wszystkie trzy redukują rozmiar payloadu, ale optymalizują różne rzeczy — szybkość, współczynnik kompresji i kompatybilność.

Jednozdaniowe podsumowanie

  • ZSTD (Zstandard): Często najlepszy balans dla API, gdy zależy Ci na niskiej latencji i przewidywalnym użyciu CPU. Dobry ratio bez spowolnienia.
  • Brotli: Często daje najmniejsze bajty na linii, zwłaszcza dla tekstowych odpowiedzi (JSON, treści podobne do HTML). Wyższe poziomy mogą kosztować więcej CPU.
  • GZIP: „Działa wszędzie” — szerokie wsparcie i łatwa operacjonalizacja, ale zwykle wolniejszy i/lub większy niż nowocześniejsze alternatywy przy podobnym zużyciu CPU.

Typowe mocne strony (i co to znaczy dla API)

Szybkość ZSTD: Świetne, gdy API jest wrażliwe na tail latency lub serwery są obciążone CPU. Potrafi kompresować na tyle szybko, że narzut często jest znikomy względem czasu sieci — szczególnie dla średnich i dużych odpowiedzi JSON.

Współczynnik Brotli: Najlepszy, gdy pasmo jest najważniejsze (klienci mobilni, drogi egress, dostawa przez CDN) i odpowiedzi są głównie tekstowe. Mniejsze payloady mogą być warte kosztu dłuższej kompresji.

Kompatybilność GZIP: Najlepsza, gdy potrzebujesz maksymalnego wsparcia po stronie klienta bez ryzyka negocjacji (stare SDK, wbudowane urządzenia, legacy proxies). To bezpieczny baseline, nawet jeśli nie jest top‑performerem.

Co naprawdę zmienia „poziom kompresji”

„Poziomy” kompresji to presety, które wymieniają czas CPU na mniejszy wynik:

  • Niższe poziomy: szybsza kompresja, większe payloady. Dobre dla API w czasie rzeczywistym.
  • Wyższe poziomy: mniejsze payloady, wolniejsza kompresja (i czasem więcej pamięci). Lepsze dla dużych, cache’owalnych odpowiedzi.

Dekompressja jest zwykle znacznie tańsza niż kompresja dla wszystkich trzech, ale bardzo wysokie poziomy mogą nadal obciążać CPU klienta/baterię — ważne na urządzeniach mobilnych.

Prosta zasada

  • Domyślny wybór: używaj ZSTD dla większości JSON/REST/GraphQL API, gdzie zależy Ci na latencji.
  • Przełącz się na Brotli: gdy optymalizujesz minimalne bajty (tekstowe odpowiedzi, dostawa przez CDN, wolne sieci) i możesz poświęcić więcej CPU.
  • Pozostań przy GZIP: gdy potrzebujesz szerokiej kompatybilności lub infrastruktura nie wspiera nowszych kodowań.

Współczynnik kompresji kontra latencja: podstawowy kompromis

Kompresja często jest sprzedawana jako „mniejsze odpowiedzi = szybsze API”. To zwykle prawda w wolnych sieciach — ale nie automatycznie. Jeśli kompresja doda wystarczająco dużo czasu CPU po stronie serwera, możesz otrzymać wolniejsze żądania pomimo mniejszej ilości bajtów.

Gdzie upływa czas

Pomaga rozdzielić dwa koszty:

  • Czas kompresji (po stronie serwera): praca przed wysłaniem pierwszego bajtu. Może bezpośrednio zwiększać czas odpowiedzi (TTFB).
  • Czas dekompresji (po stronie klienta): praca po odebraniu bajtów. Zwykle tańsza niż kompresja, ale może się liczyć na słabych urządzeniach.

Wysoki współczynnik kompresji może skrócić transfer, ale jeśli kompresja doda np. 15–30 ms CPU na odpowiedź, możesz stracić więcej niż zyskasz — szczególnie na szybkich połączeniach.

Pułapka tail‑latency pod obciążeniem

Pod obciążeniem kompresja może pogorszyć p95/p99 bardziej niż p50. Gdy użycie CPU rośnie, żądania są kolejkowane. Kolejkowanie wzmacnia małe koszty na żądanie w duże opóźnienia — średnia może wyglądać dobrze, ale najsłabsi użytkownicy cierpią.

Mierz to jak funkcję wydajności

Nie zgaduj. Zrób A/B test lub staged rollout i porównaj:

  • p50 i p95 latency (najlepiej też p99)
  • Wykorzystanie CPU i saturację na instancjach API
  • Rozmiary odpowiedzi i time‑to‑first‑byte

Testuj z realistycznymi wzorcami ruchu i payloadami. „Najlepszy” poziom to ten, który zmniejsza całkowity czas, nie tylko bajty.

Koszty CPU i pamięci po stronie serwera i klienta

Kompresja nie jest „darmowa” — przenosi pracę z sieci na CPU i pamięć po obu stronach. W API objawia się to dłuższym czasem obsługi żądania, większym użyciem pamięci i czasami spowolnieniami po stronie klienta.

Gdzie spędza się CPU

Większość CPU idzie na kompresję odpowiedzi. Kompresor szuka wzorców, buduje stany/słowniki i zapisuje zakodowany wynik.

Dekompressja jest zwykle tańsza, ale nadal istotna:

  • Serwery mogą dekompresować żądania (rzadziej dla JSON API, częściej dla uploadów lub batchy).
  • Klienci dekompresują odpowiedzi na ścieżce krytycznej przed parsowaniem JSON.

Jeśli Twoje API jest już ograniczone CPU (zajęte serwery aplikacyjne, ciężkie auth, kosztowne zapytania), włączenie wysokiego poziomu kompresji może podnieść tail latency nawet przy mniejszych payloadach.

Kwestie pamięci

Kompresja może zwiększyć użycie pamięci przez:

  • Bufory: implementacje mogą potrzebować buforów wejścia/wyjścia; większe payloady oznaczają większe bufory.
  • Pełne buforowanie vs streaming: streaming kompresja może zacząć wysyłać wcześniej i utrzymać pamięć na stałym poziomie, natomiast pełne buforowanie zwiększa szczytowe użycie pamięci na żądanie.

W środowiskach kontenerowych wyższe szczytowe użycie pamięci może skutkować OOM‑ami lub węższą gęstością instancji.

Wpływ na autoscaling i limity kontenerów

Kompresja dodaje cykle CPU na odpowiedź, zmniejszając przepustowość na instancję. To może szybciej wywoływać autoscaling i podnosić koszty. Często obserwuje się: spadek transferu, ale wzrost zużycia CPU—dlatego właściwy wybór zależy od tego, który zasób masz ograniczony.

Dlaczego szybkość dekompresji ma znaczenie dla klientów

Na urządzeniach mobilnych dekompresja konkuruje o zasoby z renderingiem, wykonywaniem JS i baterią. Format, który oszczędza kilka KB, ale dłużej się dekompresuje, może wydawać się wolniejszy, szczególnie gdy liczy się „czas do używalnych danych”.

ZSTD dla API: mocne strony, ograniczenia i dobre domyślne ustawienia

Zarządzaj implementacją
Eksportuj kod źródłowy, gdy będziesz gotowy utwardzić negocjację nagłówków i zachowanie cache.

Zstandard (ZSTD) to nowoczesny format zaprojektowany tak, aby dawać dobry współczynnik bez spowalniania API. Dla wielu API ciężkich od JSON jest silnym „domyślnym” wyborem: zauważalnie mniejsze odpowiedzi niż GZIP przy podobnej lub niższej latencji oraz bardzo szybka dekompresja po stronie klienta.

Do czego ZSTD nadaje się najlepiej

ZSTD jest wartościowy, gdy dbasz o czas end‑to‑end, nie tylko o najmniejszą liczbę bajtów. Zazwyczaj kompresuje szybko i dekompresuje ekstremalnie szybko — przydatne tam, gdzie każdy milisekundowy koszt CPU konkuruje z obsługą żądania.

Dobrze sprawdza się dla różnych rozmiarów payloadów: małe‑średnie JSONy często zyskują, a duże odpowiedzi jeszcze bardziej.

Sensowne poziomy kompresji dla API

Dla większości API zacznij od niskich poziomów (zazwyczaj 1–3). One często dają najlepszy stosunek latencji do rozmiaru.

Wyższe poziomy stosuj tylko gdy:

  • Payloady są duże (setki KB do MB)
  • Pasmo jest drogie lub ograniczone
  • Zmierzyłeś, że CPU nie jest wąskim gardłem

Pragmatyczne podejście: niski globalny default, a potem selektywne podniesienie poziomu dla kilku endpointów zwracających duże odpowiedzi.

Streaming i tryb słownika

ZSTD wspiera streaming, co może zmniejszyć szczytowe użycie pamięci i pozwolić na wcześniejsze wysyłanie danych dla dużych odpowiedzi.

Tryb słownika może przynieść duże korzyści, gdy API zwraca wiele podobnych obiektów (powtarzające się klucze, stabilne schematy). Najlepiej działa gdy:

  • Payloady są stosunkowo małe, ale częste
  • Możesz bezpiecznie zarządzać wersjonowanymi słownikami

Ograniczenia kompatybilności

Wsparcie po stronie serwera jest proste w wielu stackach, ale kompatybilność klienta może decydować. Niektóre HTTP clienty, proxy i bramy nadal nie deklarują ani nie akceptują Content-Encoding: zstd domyślnie.

Jeśli obsługujesz zewnętrznych konsumentów, trzymaj fallback (zwykle GZIP) i włącz ZSTD tylko gdy Accept-Encoding wyraźnie go zawiera.

Często zadawane pytania

Kiedy warto włączyć kompresję odpowiedzi API?

Używaj kompresji, gdy odpowiedzi są bogate w tekst (JSON/GraphQL/XML/HTML), średnie do duże, a Twoi użytkownicy korzystają z wolnych/drogich sieci lub płacisz znaczące koszty egressu. Pomiń ją (lub ustaw wysoki próg) dla malutkich odpowiedzi, już skompresowanych mediów (JPEG/MP4/ZIP/PDF) oraz dla usług obciążonych CPU, gdzie dodatkowa praca na żądanie pogorszy p95/p99 latency.

Dlaczego kompresja może spowolnić API, mimo że odpowiedzi są mniejsze?

Ponieważ wymienia ona szerokość pasma na CPU (i czasem pamięć). Czas kompresji może opóźnić moment, kiedy serwer zaczyna wysyłać bajty (TTFB), a pod obciążeniem zwiększa kolejki—często pogarszając tail latency nawet gdy średnia latencja się poprawia. Najlepsze ustawienie to takie, które skraca czas end‑to‑end, nie tylko liczbę bajtów.

Jak wybrać między ZSTD, Brotli i GZIP?

Praktyczny priorytet dla wielu API to:

  • zstd najpierw (szybki, dobry stosunek)
  • potem br (często najmniejsze dla tekstu, może kosztować więcej CPU)
  • potem gzip (najszersza kompatybilność)

Zawsze opieraj ostateczny wybór na Accept-Encoding klienta i miej bezpieczny fallback (zwykle gzip lub identity).

Jakie poziomy kompresji są rozsądnymi domyślnymi ustawieniami dla dynamicznych odpowiedzi API?

Zacznij od niskich poziomów i mierz.

  • ZSTD: poziom 1–3 (lub do 3–5) dla większości dynamicznych JSON API
  • Brotli: poziom 1–4 dla kompresji w czasie rzeczywistym; poziomy 8–11 rezerwuj dla prekompresowanych/statycznych zasobów
  • GZIP: poziom 5–6 jako dobry domyślny

Wyższe poziomy przynoszą malejące korzyści rozmiarowe, a mogą znacząco podnieść zużycie CPU i pogorszyć p95/p99.

Czy powinienem kompresować każdą odpowiedź, czy tylko powyżej pewnego rozmiaru?

Użyj progu minimalnego rozmiaru, żeby nie marnować CPU na malutkie payloady.

  • Typowy punkt startowy: 1–2 KB
  • Jeśli masz ograniczone CPU lub bardzo „rozmowne” API: rozważ 4 KB

Dostrój per endpoint, porównując zaoszczędzone bajty vs dodany czas serwera i wpływ na p50/p95/p99.

Jakie typy payloadów dobrze się kompresują (a które raczej nie)?

Skup się na treściach, które są uporządkowane i powtarzalne:

  • Świetne: JSON, GraphQL, XML, HTML, duże logi tekstowe
  • „Może”: Protobuf/MessagePack (często kompresowalne—zmierz)
  • Zwykle nie warto: JPEG/PNG/WebP, MP4, ZIP/gz, wiele PDF

Często sensowne jest włączenie kompresji tylko dla typów Content-Type przypominających tekst i wyłączenie jej dla znanych, już skompresowanych formatów.

Jak działają Accept-Encoding i Content-Encoding w API?

Kompresja powinna stosować się do negocjacji HTTP:

  • Klient wysyła Accept-Encoding (np. zstd, br, gzip)
  • Serwer odpowiada obsługiwanym Content-Encoding

Jeśli klient nie wysyła Accept-Encoding, najbezpieczniej jest zwykle nie kompresować. Nigdy nie odsyłaj Content-Encoding, którego klient nie zadeklarował, bo możesz uniemożliwić mu odczyt ciała.

Dlaczego Vary: Accept-Encoding jest ważny przy użyciu kompresji?

Dodaj nagłówek:

  • Vary: Accept-Encoding

To zapobiega sytuacji, w której CDN/proxy zcache’uje np. gzip i poda go klientowi, który tego nie zażądał lub nie potrafi go dekodować (albo dla zstd/br). Jeśli obsługujesz wiele kodowań, ten nagłówek jest konieczny dla poprawnego cache’owania.

Jakie są najczęstsze błędy związane z kompresją w produkcji?

Typowe problemy produkcyjne to:

  • Podwójna kompresja (origin kompresuje, potem gateway/CDN kompresuje ponownie)
  • Niezgodność nagłówków i ciała (Content-Encoding mówi gzip, ale ciało nie jest gzip)
  • Zła negocjacja (ignorowanie Accept-Encoding)
  • Interferencja proxy/CDN (usuwanie lub modyfikacja nagłówków)
  • Niepoprawny Content-Length przy streamingu/kompresji

Podczas debugowania przechwyć surowe nagłówki odpowiedzi i sprawdź dekompresję za pomocą znanego, poprawnego narzędzia/klienta.

Jak bezpiecznie wprowadzić, monitorować i debugować kompresję API?

Wprowadź to jako funkcję wydajności:

  • Najpierw canary: mały wycinek ruchu, potem stopniowe zwiększanie (np. 1% → 5% → 25% → 50% → 100%)
  • Miej szybki rollback (feature flag lub konfiguracja bramy)
  • Monitoruj:
    • Wykorzystanie CPU/saturację
    • p50/p95/p99 latency i TTFB
    • bajty w sieci (skompresowane vs nieskompresowane)
    • błędy/timeouts i błędy dekodowania po stronie klienta

Jeśli tail latency rośnie pod obciążeniem, obniż poziom, zwiększ próg lub przejdź na szybszy kodek (często ZSTD).

Related posts