# Redis

## Hva det er

Redis er en databaseserver som holder hele datasettet sitt i minnet som typede
strukturer, og som lar mange applikasjonsprosesser lese og endre dem over
nettverket, én kort kommando om gangen. Prosjektet beskriver seg selv som en
datastrukturserver (data structure server). Utgaven som er fritt tilgjengelig,
heter Redis Open Source, og Redis Ltd selger i tillegg Redis Cloud og Redis
Software under kommersielle lisenser.

Hver nøkkel holder én verdi av én type. Strenger er rene bytes og fungerer også
som tellere, hasher er poster med felt og verdier, lister holder på rekkefølgen
elementene kom i, sett holder unike medlemmer, og sorterte sett rangerer
medlemmene sine etter en poengsum. Listen over typer fortsetter med strømmer,
JSON-dokumenter, geospatiale indekser, tidsserier, vektorsett og
probabilistiske strukturer som Bloom-filtre og HyperLogLog.

Kommandoene virker direkte på disse typene. `INCR` legger én til en teller og
returnerer den nye verdien, `SADD` legger medlemmer inn i et sett og `SPOP` tar
ett ut, og hvilken som helst nøkkel kan få en levetid (timeout), og når den er
ute, sletter Redis nøkkelen selv. Redis kaller en slik nøkkel flyktig
(volatile). Når flere steg må skje som ett, kjører et Lua-skript inne i
serveren, og Redis garanterer at ingenting annet kjører mens det gjør det.

Minnet er der dataene bor, og disk er valgfritt. Redis kan skrive
øyeblikksbilder (snapshots), kjent som RDB-filer, eller logge hver skriving til
en fil det bare legges til i (append-only file, AOF), som spilles av på nytt ved
oppstart, eller gjøre begge deler, eller ingen av dem. Replikaer kopierer en
primærnode og kan betjene lesing. Et tak på minnet, `maxmemory`, og en policy
for utkasting (eviction policy) avgjør hva som skjer når dataene vokser ut av
plassen de fikk.

Lisensen endret seg med Redis 8. Fra den utgivelsen av tilbys Redis Open Source
med et valg mellom tre lisenser: RSALv2, SSPLv1 og AGPLv3. De to første er
lisenser med tilgjengelig kildekode (source-available), og AGPLv3 er
alternativet som er åpen kildekode.

## Hvorfor det er viktig

En webapplikasjon kjører sjelden som én prosess. Bak en lastbalanserer står
flere instanser, hver med sitt eget minne, og alt som holdes i det minnet, som
et antall innloggingsforsøk, en økt eller en side lagret i cache, finnes bare i
prosessen som tilfeldigvis behandlet forespørselen. En grense som telles på den
måten, kan unngås ved å havne på en annen instans. Redis gir alle instansene ett
felles sted for slik tilstand, raskt nok til at hver forespørsel kan sjekke det.

De atomiske kommandoene betyr like mye som farten. To instanser som hver leser
en teller, legger til én og skriver den tilbake, kan begge skrive det samme
tallet, og en økning forsvinner. `INCR` gjør lesingen og skrivingen inne i
serveren som ett steg, så hundre samtidige forespørsler etterlater telleren
nøyaktig hundre høyere. Et Lua-skript utvider den samme garantien til en kort
sekvens: en sjekk og en endring som ikke må deles opp, som å bekrefte at det
fortsatt er plass og ta plassen, kjøres som én operasjon.

Utløp er den tredje delen. Et vindu for ratebegrensning, en økt eller en
engangskode bør forsvinne når tiden er ute, og med en levetid på nøkkelen er
oppryddingen lagerets oppgave og ikke en planlagt jobbs.

Prisen er varighet (durability), og den er verdt å kjenne i tall før du velger.
Med append-only-filen og standardpolicyen med én fsync i sekundet kan et krasj
koste omtrent ett sekund med skrivinger. Med bare øyeblikksbilder, som som regel
tas med noen minutters mellomrom, sier dokumentasjonen at du må være forberedt
på å miste de siste minuttene med data. Replikering er asynkron som standard, så
en primærnode kan bekrefte en skriving og stoppe før noen replika har mottatt
den. Det gjør Redis til et godt hjem for tilstand som kan bygges opp igjen,
eller mistes uten skade, og et dårlig hjem for den eneste kopien av en ordre, en
betaling eller en saldo. Designspørsmålet som betyr mest, er hva applikasjonen
gjør når Redis ikke kan nås, eller har glemt noe den fikk beskjed om.

## Slik ser det ut i praksis

Delt tilstand for grenser, en rask sperre foran en database og en cache i et
provisjonert miljø er de tre rollene Redis har i arbeidet her.

Den første rollen finnes i en offentlig katalog over selskaper og fagfolk, der
[22 ratebegrensere holder tilstanden sin i Redis](/nb/portfolio/built-fail-closed-abuse-controls-and-rate-limiting-100/)
slik at en grense gjelder på tvers av alle instanser i stedet for å leve i én
prosess, der den ville vært lett å slippe unna. Hver ratebegrenser er bygd for
stien den beskytter, fordi et innloggingsforsøk, et søk og en masseeksport blir
misbruk ved helt ulike rater. Valget som avgjør mest, er hva som skjer når Redis
mangler: ratebegrenserne feiler lukket (fail closed), og trafikk avvises i
stedet for å slippes gjennom. Siden sier rett ut hva det koster. Et
Redis-problem blir et problem brukerne ser, akseptert med vilje, fordi
alternativet er at et Redis-problem blir et faktureringsproblem.

Den andre rollen beskrives i et notat om å selge et begrenset lager, der
[en Redis-teller som telles ned av et Lua-skript](/nb/notes/selling-limited-stock-without-overselling/)
slipper inn omtrent like mange kjøpere som det finnes enheter, og avviser alle
andre på mikrosekunder. Skriptet gjør sjekken og nedtellingen til ett steg.
Notatet er nøye med hva sperren kan love. Redis replikerer asynkront, så etter
en failover kan den nye primærnoden ha glemt noen innslipp og sluppet noen få
for mange gjennom. En betinget `UPDATE` i databasen avgjør fortsatt hvert salg,
og en avstemmingsjobb nullstiller sperren fra tallet i databasen. Når enhetene
er få og forskjellige, som nummererte seter, kan id-ene deres ligge i et
Redis-sett, og `SPOP` deler dem ut én om gangen uten noen teller å slåss om.

Den tredje er den mest hverdagslige. Et Azure-oppsett definert i Bicep
[bygger hvert miljø fra de samme malene](/nb/portfolio/provisioned-azure-infrastructure-as-code-with-bicep-container-68/),
testing, staging og product likt, og hvert av dem har Redis som cache ved siden
av en PostgreSQL Flexible Server, Container Apps som kjører api og nettstedet,
og Front Door med en WAF-policy. Fordi Redis er deklarert i malene sammen med
alt annet, er en endring av den en pull request som enhver annen endring i et
miljø.

## Hvor det går galt

Det meste av trøbbelet med Redis kommer av at et raskt, glemsomt lager behandles
som et varig, eller av antakelsen om at det husker det det ble fortalt om tid.

**En nøkkel som skulle utløpe, blir liggende for alltid.** `SET` erstatter
verdien og forkaster levetiden nøkkelen hadde, så kode som frisker opp en verdi
i cache med en vanlig `SET`, gjør en oppføring på ti minutter til en permanent
oppføring:

```text
> SET session:42 "v1" EX 600
OK
> SET session:42 "v2"
OK
> TTL session:42
(integer) -1
```

Send med levetiden ved hver skriving som trenger en, eller legg til `KEEPTTL`,
tilgjengelig siden 6.0.0, for å beholde den gamle. Den samme lekkasjen skjuler
seg i en teller bygd av `INCR` fulgt av en separat `EXPIRE`: kommer den andre
kommandoen aldri fram, utløper nøkkelen aldri. Send begge inne i én blokk med
`MULTI` og `EXEC`, eller i ett Lua-skript.

**Skrivinger begynner å returnere feil mens lesing fortsatt virker.** Dataene
har nådd `maxmemory`, og utkastingspolicyen er `noeviction`, eller en
`volatile-`-policy på et datasett der ingen nøkkel har levetid, noe som oppfører
seg på samme måte. Velg policy etter jobben: en `allkeys-`-policy for en ren
cache, der hvilken som helst nøkkel kan forsvinne, og en `volatile-`-policy bare
når nøkler i cache deler instans med nøkler som må bli. For den blandingen er
rådet i dokumentasjonen å vurdere to separate instanser.

**Skrivinger Redis bekreftet, mangler etter en omstart eller en failover.**
Årsaken er et krasj innenfor fsync-vinduet, eller en replika som ble forfremmet
før skrivingen nådde den. Slik er Redis designet, så løsningen ligger i hva som
lagres der. Lagre alt som må overleve, i en database med varige commits, sett
Redis foran den som sperre eller cache, og avstem de to fra databasens side,
slik notatet om lageret gjør.

**Hver klient stopper opp i samme øyeblikk.** Et langt Lua-skript kjører, og
Redis blokkerer all annen aktivitet på serveren så lenge et skript kjører. Hold
skriptene korte og avgrensede, og hold løkker over store samlinger utenfor dem.

**En ratebegrenser slipper trafikk gjennom under et avbrudd.** Klientkoden
behandler en feil fra Redis som «grensen er ikke nådd», så ratebegrenseren åpner
nøyaktig når lageret dens ikke kan nås. Bestem feilmodusen med vilje, og test
den ved å stoppe Redis: en ratebegrenser som vokter en kostbar sti, bør avvise,
slik de 22 ratebegrenserne over gjør, med den kostnaden at et Redis-problem blir
synlig for brukerne.

## Les mer

- [Develop with Redis](https://redis.io/docs/latest/develop/)
  — prosjektets startpunkt: kommandoer å prøve, klientbiblioteker per språk og
  datatypene.
- [Redis persistence](https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/)
  — øyeblikksbilder, append-only-filen, fsync-policyer og hva hver av dem kan
  miste.
- [Redis replication](https://redis.io/docs/latest/operate/oss_and_stack/management/replication/)
  — hvordan replikaer følger en primærnode, og hvorfor det fortsatt finnes et
  vindu for datatap.
- [Key eviction](https://redis.io/docs/latest/develop/reference/eviction/)
  — `maxmemory`, hver utkastingspolicy og råd om hvordan du velger.
- [EXPIRE](https://redis.io/docs/latest/commands/expire/)
  — levetid på nøkler, og hvilke kommandoer som fjerner den eller lar den stå.
- [Scripting with Lua](https://redis.io/docs/latest/develop/programmability/eval-intro/)
  — å kjøre et skript atomisk på serveren, og hva det blokkerer imens.

<https://engineer.company/nb/glossary/redis/>
