Cache Stampede a mobilfejlesztésben: mi ez és védelmi módszerek

Szerző: IT Sectr Megjelenés: 2026-06-13 Olvasási idő: 9 perc

Cache Stampede — a kérések lavinaszerű növekedése az adatforráshoz, amely akkor következik be, amikor több kliens vagy szál egyszerre cache hibát észlel. Mobilalkalmazásokban a Cache Stampede akkor fordul elő, amikor egy népszerű cache-bejegyzés lejár, és több száz eszköz egyszerre próbálja újratölteni az adatokat, ami a szerver túlterhelését okozza. A Medium Engineering (2024) kutatása szerint a Cache Stampede elleni helyesen konfigurált védelem akár 95%-kal csökkenti a szerver csúcsterhelését.

Főbb pontok

  • Cache Stampede — kérések lavinája az adatforráshoz egy népszerű cache-bejegyzés egyidejű lejáratakor.
  • Probabilistic Early Expiration — minden kliens véletlenszerűen ellenőrzi az aktualitást a TTL lejárata előtt.
  • Mutex zárolás — az első kérés zárolást kap a cache frissítésére, a többiek várnak.
  • XFetch — adaptív algoritmus, amely a hátralévő élettartam alapján kiszámítja a korai frissítés valószínűségét.
  • Megelőző frissítés — egy háttérfeladat frissíti a cache-t a lejárat előtt, kiküszöbölve a cache hibát.

Mi az a Cache Stampede?

Cache Stampede (más néven cache thundering herd) — olyan helyzet, amikor nagyszámú kérés egyszerre cache hibát észlel, és az adatforráshoz irányul. Ez csúcsterhelést hoz létre, amely a rendszer degradációjához vagy teljes leállásához vezethet.

Tipikus forgatókönyv: egy mobilalkalmazás egy közös terméklistát cache-öl TTL = 5 perccel. 14:00-kor a cache lejár. 500 aktív felhasználó egyszerre nyitja meg a katalógust, mindegyik üres cache-t talál, és mind az 500 kérés a szerverre zúdul. Az adatbázis vagy külső API nem birkózik meg a hirtelen áradattal, az oldal 10-15 másodpercig töltődik, a kérések egy része timeoutot kap.

A Vattani et al. (SOCC, 2015) adatai szerint a Cache Stampede bármely cache-rétegben előfordul lejáró bejegyzésekkel, beleértve a CDN-t, Redis-t, Memcached-et, a böngésző HTTP-gyorsítótárát és az alkalmazás memóriabeli gyorsítótárát. Minél népszerűbb a bejegyzés — annál nagyobb a potenciális kár a stampede-től.

python
def mutex_get(key, lock_timeout=5):
    cached = cache.get(key)
    if cached is not None:
        return cached
    if lock.acquire(key, lock_timeout):
        data = source.load(key)
        cache.set(key, data)
        lock.release(key)
        return data
    sleep(0.01)
    return mutex_get(key, lock_timeout)

Ez a példa a mutexen keresztüli védelmet mutatja: csak az első szál frissíti a cache-t, a többiek várják a kész eredményt. A zárolási mechanizmus — a legegyszerűbb, de hatékony módja a stampede megelőzésének mérsékelt terhelések esetén.

Miért alakul ki a kérések lavinája cache hiány esetén

A TTL tömeges lejárata — a Cache Stampede fő oka. Amikor egy népszerű cache-bejegyzés minden kliens számára azonos TTL-lel rendelkezik, mindegyik egyszerre észleli annak hiányát. Ez jellemző a közös adatokra: valutaárfolyamok, országlista, az alkalmazás alapkonfigurációja.

Összeomlás a szerver újraindításakor — ha a Redis vagy Memcached alaphelyzetbe áll, a teljes cache üres lesz. Az első forgalmi csúcsnál az összes kérés az adatbázisba megy. Az Amazon AWS Architecture Blog (2024) szerint ajánlott a cache felmelegítése újraindítás után: a népszerű bejegyzések fokozatos betöltése a hirtelen terhelésnövekedés elkerülése érdekében.

Hiba az érvénytelenítési logikában — amikor a fejlesztő egy elem megváltoztatásakor az összes felhasználó számára törli a cache-t. Például egy híralkalmazásban egy cikk közzétételekor a teljes hírlista érvénytelenítve lesz. Több száz felhasználó egyszerre üres cache-t talál — és a szerver összeomlik. A pontszerű érvénytelenítés (csak a módosított bejegyzésé) kiküszöböli ezt a problémát.

Az alkalmazás hideg indítása — mobileszközökön a cache csak a folyamat memóriájában létezik. Az alkalmazás újraindítása után a cache üres, és minden kérés a szerverre megy. Megoldás: a cache mentése lemezre a munkamenetek között (Room, SQLite) és a kulcsfontosságú adatok előzetes betöltése indításkor.

Mutex zárolás a cache védelmére

A módszer ötlete — cache hiány esetén az első szál zárolást (lock) szerez és megkezdi az adatok betöltését a forrásból. A többi szál megvárja a zárolás feloldását, és beolvassa a frissített cache-t. A zárolás megvalósítható Redis SETNX, ZooKeeper vagy akár memóriabeli mutex segítségével.

A kulcsparaméter — lock_timeout. Ha túl rövid, az első szál nem biztos, hogy időben frissíti a cache-t, és a második szál is megpróbálja betölteni az adatokat, létrehozva egy mini-stampede-et. Ha túl hosszú — a kliensek a szükségesnél tovább várnak. Ajánlott érték: 2-5 másodperc tipikus adatbázis-lekérdezésekhez.

Az egyszál problémája — az első szál meghibásodásakor (kivétel, timeout) a zárolás foglalt marad, és az összes többi szál a lock_timeout lejáratáig vár. Megoldás: a zárolás törlése a finally blokkban. A Redis Best Practices (2025) szerint ajánlott Lua szkriptek használata a zárolások atomi beállításához és feloldásához.

lua
-- Atomic lock acquisition script for Redis
if redis.call("SET", KEYS[1], "locked", "NX", "EX", ARGV[1]) then
    return true
else
    return false
end

Ez a Lua szkript atomi módon ellenőrzi és beállítja a zárolást TTL-lel. Az atomicitás garantálja, hogy két egyidejű kérés nem kaphat zárolást — csak az egyik, ami teljes mértékben megakadályozza a Cache Stampede-et.

Probabilistic Early Expiration és az XFetch algoritmus

Probabilistic Early Expiration (PEE) — egy elegáns, zárolások nélküli módszer. Az ötlet az, hogy minden kliens bizonyos valószínűséggel újraszámítja a cache-t a tényleges lejárat előtt. Minél közelebb van a bejegyzés a lejárathoz — annál nagyobb a valószínűség. Ez természetesen elosztja az újratöltéseket az időben, és a csúcsterhelés kisimul.

Az XFetch algoritmus — Vattani, Chierichetti és Panconesi (2015) javasolta. A valószínűség képlete: p = max(0, 1 - (beta * age / ttl)), ahol beta — az egyenetlenségi paraméter (általában 1.0). age = 0 esetén → p = 1 (garantált frissítés nulla életkor esetén, ami helytelen). Ezért módosítást használnak: p = max(0, (ttl - age) / (ttl * beta)).

Az Etsy Engineering (2024) adatai szerint az XFetch bevezetése a Memcached-ben a napi stampede-események számát 12-ről 0-ra csökkentette, az adatbázis csúcsterhelése pedig 78%-kal esett vissza. A randomizált frissítés nem igényel zárolást és szinkronizációt, ami ideálissá teszi az XFetch-et a mikroszolgáltatás-architektúrákhoz.

Mobil kliensek esetén a PEE alkalmazható az alkalmazás oldalán: a kliensek véletlenszerűen kezdeményezik a cache frissítését a hivatalos lejárat előtt. Például age > 80% TTL esetén minden kérés 20% valószínűséggel frissíti az adatokat. Az elosztott mobileszközök természetes zajt hoznak létre, ami tovább csökkenti a szinkron becsapódás valószínűségét.

Cache megelőző frissítése

Az approach ötlete — a cache frissítése a lejárat előtt, teljesen kiküszöbölve a cache hibát. Egy háttérfolyamat (cron, Kubernetes scheduler) időszakosan ellenőrzi a népszerű kulcsokat, és új TTL-lel írja felül őket. A kliensek mindig érvényes cache-t találnak — a stampede definíció szerint lehetetlen.

Time-To-Refresh (TTR) — paraméter, amely meghatározza, hogy mennyivel a lejárat előtt kezdődjön a frissítés. Például TTL = 300 másodperc, TTR = 60 másodperc: a 240 másodpercnél a háttérfolyamat felülírja az adatokat. Ez garantálja, hogy a kliensek soha nem látnak üres cache-t.

A megelőző frissítés hátránya — állandó terhelés az adatforráson, még akkor is, ha senki sem kéri a bejegyzést. Ritkán használt adatok esetén ez nem hatékony. Megoldás: a kulcshoz érkező kérések gyakoriságának (access frequency) nyomon követése és csak a népszerű bejegyzések frissítése. Adaptív TTR — egy fejlett technika, ahol a frissítési intervallum dinamikusan, a kérések előzményei alapján kerül kiszámításra.

Melyik védelmi módszert válasszuk

Mutex zárolás — mérsékelt terhelésű rendszerekhez alkalmas (kulcsonként 10 000 rps-ig). Könnyen implementálható, és garantálja, hogy a forrás csak egy frissítési kérést kap. Hátrány — a zárolások késleltetést okoznak a várakozó szálak számára.

Probabilistic Early Expiration / XFetch — optimális a nagy terhelésekhez és elosztott rendszerekhez. Nem igényel zárolást, vízszintesen skálázódik, a csúcsterhelés természetesen kisimul. Redis klaszterekhez és Memcached-hez ajánlott, ahol több ezer kliens van.

Megelőző frissítés — ideális a kritikus fontosságú adatokhoz kiszámítható hozzáférési mintával (konfigurációk, segédkönyvek, alapvető metaadatok). További infrastruktúrát igényel a háttérfolyamathoz.

Lemez cache a kliens oldalán — a mobilalkalmazások számára a legfontosabb védelmi szint. Még ha a szerver cache érvénytelenítve is van, a mobil kliens megjelenítheti az adatokat a lemezről, amíg az új kérés végrehajtódik. Room + Stale-While-Revalidate — a Google (2025) által ajánlott minta, amely teljesen kiküszöböli a Cache Stampede-et a kliens oldalon.

Gyakran Ismételt Kérdések

Miben különbözik a Cache Stampede a DDoS-támadástól?

Cache Stampede — a legitim kliensek normál viselkedésének eredménye, akik egyszerre üres cache-t találnak. A DDoS — szándékos támadás. A Stampede-hez elegendőek a védelmi algoritmusok; a DDoS-hez további forgalomszűrési infrastruktúra szükséges.

Hogyan ellenőrizhető, hogy a rendszerben van-e Cache Stampede?

Figyelje az RPS grafikonokat az adatforráson (adatbázis, API). Ha rendszeres terhelési csúcsokat lát, amelyek szinkronban vannak a cache lejáratának pillanatával — ez Cache Stampede. Adjon hozzá cache miss rate metrikát minden népszerű kulcshoz.

Kialakulhat-e Cache Stampede a mobil kliens oldalán?

Igen, ha több szál az alkalmazásban közös memóriabeli cache-t használ. Például 10 korutin egyszerre kéri a felhasználói profilt — az első zárol, a maradék 9 megduplázhatja a kérést. A Kotlin kotlinx.coroutines könyvtár ezt a CoroutineCache vagy Flow.distinctUntilChanged segítségével oldja meg.

Milyen beta paramétert válasszunk az XFetch-hez?

beta = 1.0 — az alapértelmezett érték, amely egyenletes eloszlást biztosít az újraszámításokhoz. Agresszívabb védelemhez (kisebb cache hiba valószínűség) növelje a beta-t 1.5–2.0-ra. A forrás erőforrásainak megtakarításához — csökkentse 0.5-re. Ajánlott tartomány: 0.8–1.2.

Működik a Probabilistic Early Expiration CDN-nel?

Igen, a Cache-Control: stale-while-revalidate és Cache-Control: stale-if-error mechanizmuson keresztül. A CDN visszaadja a régi verziót, és a háttérben frissíti a cache-t, ami a PEE CDN-ekvivalense. A Cloudflare és a Fastly 2023 óta támogatja ezeket az irányelveket.

Összefoglaló

  • Cache Stampede — kérések lavinája az adatforráshoz tömeges cache hiány esetén, ami a rendszer leállását okozhatja.
  • Fő ok — egy népszerű bejegyzés TTL-jének egyidejű lejárata több kliensnél vagy szálnál.
  • Mutex zárolás — az első szál frissíti a cache-t, a többiek várnak; egyszerű, de késleltetést okoz.
  • Probabilistic Early Expiration — minden kliens véletlenszerűen újraszámítja a cache-t a lejárat előtt, kisimítva a csúcsokat.
  • XFetch — adaptív algoritmus, amely a hátralévő élettartam és a beta paraméter alapján számítja ki az újraszámítás valószínűségét.
  • Megelőző frissítés — háttérfolyamat felülírja a cache-t a lejárat előtt a népszerű kulcsoknál, kiküszöbölve a cache hibát.
  • Legjobb védelem mobilalkalmazásokhoz — a lemez cache (Room) kombinációja a Stale-While-Revalidate-tel és a szerveroldali védelem az XFetch-en keresztül.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is