Skutečná cena mikroslužeb: výkon, složitost a režie

Mikroslužby slibují flexibilitu, nezávislé nasazování a snadné škálování týmů. Ve skutečnosti však každé síťové volání přidává latenci, zvyšuje režii a komplikuje dostupnost. Tento článek ukazuje, proč i jednoduché workflow může být v mikroslužbách pomalejší než v monolitu, doplněno o čísla, kód a praktické tipy pro rozhodování mezi architekturami.
Nálepky:
Mikroslužby se v profesionálním vývoji prezentují jako „moderní“ architektura. Slíbí škálovatelnost, nezávislé nasazování a agilitu. Nicméně realita je taková, že síťové volání a distribuovaná komunikace mají výraznou režii, kterou je třeba zvažovat už při návrhu aplikace. Zjednodušené modely výkonu můžeme vyjádřit i jednoduchým „matematickým důkazem“, který porovnává monolitickou architekturu a mikroslužby.
Základní latence: proces vs. síť
První důležitý fakt je rozdíl mezi voláním funkce přímo v procesu a voláním služby přes síť:
Function call: 0.001ms (přímé volání v aplikaci)
HTTP request within same datacenter: 1–5ms (síťové volání)Code language: PHP (php)
To znamená, že síťové volání je 1 000× až 5 000× pomalejší než lokální volání. To samé platí pro volání mezi kontejnery nebo přes service mesh – i v datacentru je síť výrazně pomalejší než volání uvnitř procesu.
Reálný scénář: e‑shopový checkout
Abychom pochopili dopad tohoto rozdílu v latenci, podívejme se na typický pracovní tok během nákupu v e‑shopu:
Potřebné kroky:
- Validovat uživatelskou session
- Ověřit skladovou dostupnost
- Spočítat cenu dopravy
- Zpracovat platbu
- Vytvořit objednávku
- Odeslat potvrzovací e‑mail
V monolitu může takový proces vypadat zhruba takto:
6 volání funkcí × 0.001ms = 0.006ms
Database queries: 3 × 2ms = 6ms
Externí API (platba): 150ms
----------------------------------
Celkem: ~156ms
V mikroslužbové architektuře jsou obdobné kroky něco jako:
await userService.validateSession()
await inventoryService.checkStock()
await shippingService.calculate()
await paymentService.process(paymentInfo)
await orderService.createOrder(orderData)
await notificationService.sendConfirmation()Code language: JavaScript (javascript)
Každý z těchto volání typicky zahrnuje:
- síťovou latenci (~1-2 ms),
- serializaci/deserializaci (~0.5 ms),
- vlastní zpracování (~1-2 ms).
Pro šest služeb to dává přibližně:
6 služeb × ~3ms síťové režie = 18ms
DB dotazy: 6 × 2ms = 12ms
Externí API (platba): 150ms
-------------------------------------
Celkem: ~180ms
I v tomto jednoduchém příkladu je mikroslužbová verze přibližně o 15 % pomalejší než monolit.
„N+1 services“ – analogie N+1 dotazů
V databázových systémech známe problém N+1 dotazů, kdy místo jednoho optimalizovaného dotazu generujeme N dotazů kvůli špatnému přístupu. Podobně u mikroslužeb vzniká problém N+1 služeb, když logika vyžaduje řetězení služeb, které se nedají paralelizovat kvůli závislostem na datech:
-- efektivní monolitický dotaz
SELECT users.*, orders.*, recommendations.*
FROM users
LEFT JOIN orders ON orders.user_id = users.id
LEFT JOIN recommendations ON recommendations.user_id = users.id
WHERE users.id = $1
LIMIT 10;
Prototypická implementace v mikroslužbách:
// 1. Získat uživatele
const user = await userService.getUser(userId); // ~3ms
// 2. Získat objednávky
const orders = await orderService.getOrders(userId); // ~3ms
// 3. Získat doporučení
const orderIds = orders.map(o => o.id);
const recommendations =
await recommendationService.getByOrders(orderIds); // ~3msCode language: JavaScript (javascript)
Sekvenční provedení dává ~9 ms namísto ~5 ms v monolitu, tedy ~80 % pomalejší. A to opět za předpokladu absolutně perfektní sítě bez chyb nebo retry logiky.
Benchmark: jednoduchá CRUD aplikace
Autor benchmarku porovnával testovací aplikaci se čtyřmi entitami při zatížení 10 000 req/s:
| Metrika | Monolit | Mikroslužby | Změna |
|---|---|---|---|
| p50 latency | 12 ms | 18 ms | +50 % |
| p95 latency | 25 ms | 45 ms | +80 % |
| p99 latency | 50 ms | 120 ms | +140 % |
| Throughput | 10 000 | 8 500 | -15 % |
| CPU usage | 45 % | 65 % | +44 % |
| Memory usage | 512 MB | 2 GB | +300 % |
| Network I/O | 50 MB/s | 180 MB/s | +260 % |
Tato čísla ukazují, že největší rozdíl je v tail latency (p99) a také v režii zdrojů – mikroslužby mohou vyžadovat výrazně více paměti, CPU i síťové propustnosti, i když teoreticky „škáluje“.
Rozklad jednoho volání služby
Abychom pochopili, co se děje pod pokličkou, rozebereme i anatomii jednoho síťového volání:
DNS resolution: 0.1ms (cached)
TCP handshake: 0.5ms
TLS handshake: 1.0ms
HTTP headers: 0.2ms
Request serialization: 0.3ms
Network transmission: 0.5ms
Service processing: 0.5ms
Response serialization: 0.3ms
Network transmission: 0.5ms
Response parsing: 0.2ms
--------------------------------------
Celkem: ~3.0msCode language: CSS (css)
Stejná operace jako lokální volání má ~0.001 ms. Latence síťového volání tak může být řádově tisíckrát až desetitisíckrát vyšší.
Kaskádové selhání a dostupnost
Další výrazný efekt mikroslužeb je řetězení availability. Pokud každá služba má 99,9 % dostupnost, celý řetězec pěti služeb má:
0.999^5 ≈ 0.995 = 99.5 %
To znamená přibližně 3,6 hodiny výpadků měsíčně namísto ~43 minut u jediné služby s 99,9 % dostupností. A toto je čistě teoretický scénář bez chyb v síti, bez deployů a bez retry logiky.
Skryté náklady: databáze a spojení
I když mikroslužby slibují oddělení dat, v praxi často sdílejí stejnou databázi. To přináší další režii:
- každá služba má vlastní connection pool, který rychle vyčerpá limity DB serveru,
- více paralelních spojení vede k vyšší lock contention,
- cache se chová hůře díky různorodým přístupovým vzorcům.
Příklad z testů ukazuje, že monolit může používat 20-30 spojení s nízkou latencí, zatímco mikroslužby mohou vytvářet 60-80 spojení, což zvyšuje čekací doby zámků a celkovou latenci dotazů.
Přínos vs. náklady
Mikroslužby řeší organizaceční problémy, ne technické, což znamená, že:
- umožňují nezávislé deploye týmů,
- oddělují odpovědnosti,
- podporují technologickou diverzitu.
Na druhé straně, pokud tyto organizační důvody neexistují, mikroslužby často přinášejí více problémů než řešení, což je potvrzeno i v různých diskusích okolo tohoto tématu.
Kdy mikroservisy dávají smysl
Prakticky je vhodné přejít na mikroslužby až když:
- máte velké týmy s nezávislými doménami,
- jednotlivé části systému vyžadují odlišné škálování,
- existují jasné hranice a kontrakty mezi moduly,
- organizační benefity převáží technické náklady.
Pokud tomu tak není, často je lepší zůstat u modulárního monolitu nebo hybridního modelu.
Závěr
Distribuované systémy mají svou vlastní logiku: mikroslužby nezmizí síťovou latenci, jen ji přesunou a znásobí, pokud jich je více a jejich volání nejsou efektivně navržena. Každé další volání služby přidává režii, zvyšuje latenci a komplikuje dostupnost.
Benchmarky, kódové příklady a simulace ukazují, že i relativně jednoduché workflow může být v mikroslužbách pomalejší a náročnější na zdroje než v monolitu, a to nezávisle na rychlosti samotného kódu nebo infrastruktury.
Mikroslužby přinášejí hlavně organizační benefity: umožňují nezávislé deploye, oddělují odpovědnosti a podporují technologickou diverzitu. Pokud tyto důvody neexistují, náklady v podobě vyšší latence, složitosti a režie často převýší přínosy.
Pro mnoho projektů proto platí: začněte s modulárním monolitem, udržujte čistou architekturu a mikroslužby zavádějte teprve tam, kde organizační nebo škálovací důvody opravdu dávají smysl.
Zdroj: https://dev.to/polliog/microservices-are-killing-your-performance-and-heres-the-math-21op