Caching er et centralt begreb inden for datalogi. Ideen bag en cache (udtales "cash" /ˈkæʃ/ KASH) er enkel: hvis det er dyrt (i tid eller ressourcer) at beregne eller hente et resultat, kan man gemme en kopi, så efterfølgende forespørgsler kan få svaret meget hurtigere. Ofte findes to slags lagringsmedier: ét, der er stort, men relativt langsomt at få adgang til, og ét, der er lille, men meget hurtigt. Cachen placeres typisk mellem klienten og den langsommere lagerenhed og holder kopier af ofte brugte data. Dermed bliver den gennemsnitlige adgangstid reduceret, fordi mange forespørgsler kan besvares direkte fra den hurtige kopi.
Hvordan fungerer en cache?
Når et system forsøger at læse en bestemt enhed (f.eks. en blok eller et objekt), undersøger cachen først, om der findes en kopi. Hvis kopien findes, kaldes det et cache-hit — ellers et cache-miss. Ved et miss hentes data fra den langsommere kilde, og resultatet kan indsættes i cachen til senere genbrug. De vigtigste mål for en cache er derfor:
- Hitrate: andelen af forespørgsler, der resulterer i hits.
- Miss-penalty: hvor dyrt (i tid) det er at hente data ved et miss.
- Latens: hvor hurtigt cachen kan levere et svar.
Referencelokalitet
Caches virker godt, fordi programmer ofte viser referencelokalitet: når et program tilgår én blok, er det sandsynligt, at det snart tilgår data tæt på denne blok (lokalitet i tid og rum). Denne egenskab øger chancen for, at gemte kopier bliver genbrugt.
Buffer vs. cache
En buffer ligner en cache, men forskellen ligger i ejerforhold og forventning: en buffer bruges ofte som midlertidig lagerplads, hvor klienten ved, at data er buffered, og programmet styrer bufferen eksplicit. En cache er typisk transparent for klienten — klienten behøver ikke at vide, at der findes en cache.
Erstatningsstrategier (eviction policies)
Når cachen er fuld, skal en gammel post fjernes for at give plads til ny data. Almindelige strategier er:
- LRU (Least Recently Used): fjern den post, der er brugt længst tid siden.
- LFU (Least Frequently Used): fjern den, der er brugt mindst hyppigt.
- FIFO (First In, First Out): fjern den ældste post.
- Random: vælg en post tilfældigt — nemt at implementere og nogen gange tilstrækkeligt.
- CLOCK: en effektiv approximation af LRU, der er billig i implementering.
- Adaptive algoritmer som ARC kombinerer fordele fra flere strategier for bedre performance under varierede arbejdsbelastninger.
Skrivemodeller
Ved skrivning er der flere modeller, hver med sine trade-offs:
- Write-through: Skrivninger skrives både til cachen og til den bagvedliggende lagerenhed straks. Fordel: konsistens. Ulempe: langsommere skrivninger.
- Write-back (eller write-behind): Skrivninger markeres i cachen som “dirty” og skrives senere tilbage til hovedlageret. Fordel: hurtigere skrivninger; ulempe: kompleksitet ved flush og større risiko for tab ved fejl.
- Write-around: Skrivninger springer ofte cachen over og går direkte til hovedlageret, hvilket kan forhindre at cache fyldes med data, der sjældent læses.
Cache-arkitektur og associativitet
Caches kan organiseres forskelligt:
- Direct-mapped: hver blok i hovedlageret har én bestemt plads i cachen — enkel og hurtig, men kan give konflikter.
- Fully associative: en blok kan placeres i enhver cache-slot — fleksibelt, men kræver søgning i mange poster.
- Set-associative: kompromis, hvor cachen er delt i sæt, og hver blok kan placeres i et sæt af N slots (f.eks. 4-værelses set-associative).
Typer og lag af caches
Caching anvendes i mange niveauer og i forskellige former:
- CPU-caches (L1, L2, L3): små, hurtige caches tæt på processoren.
- Disk- og sidecacher (f.eks. OS page cache): reducerer disk-I/O.
- Browser-caching: gemmer ressourcer som HTML, CSS og billeder lokalt for at minimere netværksanmodninger.
- Reverse proxy / CDN: cache af webindhold tættere på brugerne for at reducere latenstid og belastning på oprindelsesserveren.
- Applikationscaches (f.eks. in-memory stores som Redis eller Memcached): bruges til hyppigt brugte objekter i dit backend.
Konsistens, invalidering og problemer
Et af de sværeste aspekter ved caching er at holde data konsistente:
- Cache-invalidation er nødvendigt, når data i den langsomme kilde ændres — strategier inkluderer eksplicit invalidering, TTL (time-to-live) og write-through for at minimere inkonsistens.
- I distribuerede systemer kræver cache-konsistens protokoller (f.eks. MESI i multiprocessorsystemer) eller koordinering mellem noder.
- Andre problemer: cache stampede (mange klienter forsøger samtidig at genopbygge en udløbet cache-post), thundering herd, samt memory leaks hvis entries aldrig fjernes uden TTL.
Ydeevneovervejelser og praktische råd
- Mål din hitrate og latency — det er de vigtigste mål for at vurdere cache-effektivitet.
- Vælg erstatningsstrategi ud fra arbejdsprofilen: LRU er ofte et godt udgangspunkt, men LFU kan være bedre for stabilt populære objekter.
- Brug cache-aside-mønsteret i applikationer: applikationen tjekker cachen først, henter fra kilden ved miss og opdaterer cachen.
- Håndter cache-stampede ved at bruge låse, jitterede TTL'er eller "singleflight" mekanismer, som koordinerer genopbygning af en post.
- Overvej forudindlæsning (prefetching) for arbejdsbelastninger med stærk prædiktiv lokalitet, men undgå over-prefetching, som kan forurene cachen.
- Balancer størrelse og adgangstid: større caches kan øge hitrate, men også gøre lookup langsommere og dyrere i hardware.
Opsummering
En cache er et midlertidigt lager med kopier af ofte anvendte data, designet til at reducere den gennemsnitlige adgangstid. Caches bygger på lokalitet i programmer, bruger forskellige arkitekturer og erstatningsstrategier og findes i mange varianter (CPU, disk, web, applikation). De største udfordringer er korrekt invalidering, konsistens og håndtering af corner-cases som cache-stampede. Med de rette strategier og målinger kan caching give dramatisk bedre ydeevne og effektivitet.

