Et API med mye trafikk caches ved at du først måler, og deretter legger et cache-aside-lag i Redis foran de tregeste og mest leste endepunktene, med eksplisitte TTL-er, invalidering ved skriving og vern mot cache stampede. Bruk Elasticsearch til søk og filtrerte lister som hoveddatabasen håndterer dårlig, og cache aldri brukerspesifikke eller strengt konsistente data uten et bevisst design.
Mål P95-latensen før du legger til cache
Gjennomsnitt skjuler forespørslene brukerne klager på. Følg P95- og P99-latens per endepunkt, sammen med antall forespørsler, slik at du ser hvilke ruter som både er trege og mye brukt.
Finn deretter ut hvor tiden går. Et spor (trace) eller en enkel tidsfordeling per forespørsel viser som regel om kostnaden er en treg spørring, gjentatte spørringer, et kall til et eksternt API eller serialisering. Å cache et svar der det egentlige problemet er en manglende indeks, skjuler bare problemet til neste gang cachen bommer.
- Registrer P50, P95 og P99 per endepunkt, ikke bare ett samlet tall
- Ranger endepunktene etter antall forespørsler ganget med latens for å se hvor caching lønner seg mest
- Sjekk forholdet mellom lesing og skriving, fordi data som leses langt oftere enn de endres, er de beste kandidatene
- Rett manglende indekser og N+1-spørringer før du cacher rundt dem
Bruk cache-aside som standardmønster
Med cache-aside sjekker applikasjonen Redis først. Ved treff returnerer den den cachede verdien. Ved bom leser den fra databasen, skriver resultatet til Redis med en TTL og returnerer det.
Mønsteret beholder databasen som sannhetskilde og svikter på en trygg måte. Hvis Redis er treg eller utilgjengelig, faller applikasjonen tilbake på databasen, med et kort tidsavbrudd og en circuit breaker, slik at en cache som sliter, ikke gjør alle forespørsler tregere.
Utform nøklene med omhu. Ta med ressurstypen, identifikatoren, API-versjonen og alle parametere som endrer svaret, for eksempel språk eller side. Et forutsigbart nøkkelskjema er det som gjør målrettet invalidering mulig senere.
Sett TTL etter hvor gamle dataene kan være, og invalider ved endring
En TTL er en forretningsbeslutning skrevet som et tall. Spør hvor gamle dataene kan være før en bruker eller et system lenger ned i kjeden tar skade, og sett TTL-en ut fra svaret. Stillingen i en kamp som pågår, og en arkivert artikkel tåler svært ulik grad av utdaterte data.
Hvis du bare stoler på utløp, serverer du utdaterte data til TTL-en går ut. For data som din egen applikasjon endrer, sletter eller overskriver du nøkkelen etter at skrivingen er bekreftet (commit), helst fra en hendelse som sendes etter transaksjonen, slik at cachen aldri holder data som databasen har rullet tilbake.
Legg til et lite tilfeldig tillegg (jitter) på TTL-ene, slik at nøkler som er skrevet samtidig, ikke utløper i samme øyeblikk.
- Kort TTL på noen sekunder for data som endres raskt, der litt utdaterte data er akseptabelt
- Lengre TTL pluss invalidering ved skriving for data du kontrollerer, og som sjelden endres
- Versjonerte nøkler, der et nytt versjonsnummer invaliderer en hel gruppe nøkler på én gang
Beskytt databasen mot cache stampede
En cache stampede oppstår når en populær nøkkel utløper og mange samtidige forespørsler bommer på én gang, slik at alle sender den samme kostbare spørringen til databasen. På et API med mye trafikk kan dette overbelaste databasen akkurat når trafikken topper seg.
Kombiner to eller flere av disse forsvarene på de mest brukte nøklene dine.
- Sammenslåing av forespørsler: én forespørsel bygger nøkkelen på nytt under en kort lås i Redis, satt med NX og en utløpstid, mens de andre venter litt eller serverer den forrige verdien
- Stale-while-revalidate: lagre et mykt utløp inne i verdien, fortsett å servere den utdaterte kopien etter det, og oppdater i bakgrunnen
- Tidlig sannsynlighetsbasert oppdatering: bygg av og til en mye brukt nøkkel på nytt før den utløper, med en sannsynlighet som øker jo nærmere utløpet den kommer
- Forvarming: fyll kjente, mye brukte nøkler før en planlagt trafikktopp, for eksempel en stor direktesendt begivenhet
Bruk Elasticsearch til søk og lister, ikke som en generell cache
Redis er best til oppslag på nøkkel og verdi: ett enkelt objekt, et beregnet fragment, en teller for hastighetsbegrensning. Elasticsearch passer til en annen jobb: fritekstsøk, fasetterte filtre og sorterte lister som er kostbare å beregne i en relasjonsdatabase.
Behandle Elasticsearch-indeksen som en lesemodell som fylles fra hoveddatabasen, via endringshendelser eller en planlagt synkronisering, og godta at den bare blir konsistent etter hvert. La databasen være autoritativ for skriving og for alt som må være nøyaktig.
På sportsmedieplattformen til NorthStar Network, som betjener 50M+ brukere i måneden, bygde utviklerne våre om kritiske API-er og utformet cachingen på nytt i Redis og Elasticsearch, noe som reduserte P95-responstidene.
Vit hva du ikke skal cache
Den dyreste cachefeilen er å servere én brukers data til en annen. Gå gjennom hvert cachet endepunkt for å sjekke at identiteten er med i nøkkelen, og test det med to ulike kontoer før lansering.
- Svar som avhenger av identiteten eller tillatelsene til den som kaller API-et, med mindre nøkkelen inkluderer brukeren eller rollen
- Data som må være strengt konsistente, som saldoer, lagerbeholdning i kassen eller alt som brukes i autorisasjonsbeslutninger
- Endepunkter for skriving, engangstokener og alt som har sideeffekter
- Endepunkter med lite trafikk, der en cache gir mer kompleksitet og en ny måte å feile på for liten gevinst
- Svært store nyttelaster som skyver ut mange mindre og mer brukte nøkler
Gjør cachen observerbar, ellers kan du ikke stole på den
Følg treffraten per nøkkelprefiks, minnebruk og utkastelser i Redis, latensen på Redis-kommandoer og belastningen på databasen, ved siden av API-ets P95 i det samme dashbordet. Når P95 endrer seg, bør du i løpet av minutter kunne se om det var cachen, databasen eller en tjeneste lenger opp i kjeden som var årsaken.
Sett opp varsler ved et plutselig fall i treffraten, som ofte betyr at en utrulling endret et nøkkelformat, og ved økende utkastelser, som betyr at cachen er for liten for datasettet den skal holde.
Vi tar på oss arbeid med API-ytelse som en avgrenset blokk: måling, ny utforming av cachelaget og overlevering av dashbord og driftsinstrukser til teamet som drifter det.
Det viktigste
- Mål P95 per endepunkt, og rett problemer i spørringene før du legger til en cache.
- Cache-aside i Redis er det tryggeste standardvalget, fordi databasen forblir sannhetskilden.
- Sett TTL ut fra hvor utdaterte dataene kan være, og invalider ved skriving for data du kontrollerer.
- Beskytt mye brukte nøkler mot cache stampede med låsing, stale-while-revalidate eller forvarming.
- Bruk Elasticsearch som en lesemodell som blir konsistent etter hvert, for søk og lister, ikke som en generell cache.
Spørsmål og svar
Bør jeg bruke Redis eller Elasticsearch til å cache API-svar?
Bruk Redis til caching av objekter, beregnede fragmenter og tellere etter nøkkel og verdi, fordi oppslag på nøkkel er raske og enkle å invalidere. Bruk Elasticsearch når det kostbare er søk, filtrering eller sortering på tvers av mange poster, og behandle indeksen som en lesemodell heller enn en cache.
Hva er en god TTL for API-svar?
Det finnes ingen universell verdi. Sett hver TTL ut fra hvor utdaterte dataene kan være uten at det skader en bruker, bruk noen sekunder for data som endres raskt, og kombiner lengre TTL-er med invalidering ved skriving. Legg til tilfeldig jitter, slik at nøkler som hører sammen, ikke utløper samtidig.
Hvordan forhindrer jeg cache stampede i Redis?
La bare én forespørsel bygge en utløpt nøkkel på nytt ved å ta en kort lås med SET og NX-opsjonen, og la de andre forespørslene vente litt eller servere den forrige verdien. Stale-while-revalidate og tidlig oppdatering av mye brukte nøkler reduserer i utgangspunktet antallet harde utløp.