Programmering

Hvornår skal man bruge en CRDT-baseret database

Roshan Kumar er senior produktchef hos Redis Labs.

Bøjning af konsistensen og tilgængeligheden som beskrevet af CAP-sætningen har været en stor udfordring for arkitekterne for geodistribuerede applikationer. Netværkspartition er uundgåelig. Den høje latenstid mellem datacentre resulterer altid i en vis afbrydelse mellem datacentrene i en kort periode. Således er traditionelle arkitekturer til geodistribuerede applikationer designet til enten at opgive datakonsistens eller få et hit på tilgængelighed.

Desværre har du ikke råd til at ofre tilgængeligheden for interaktive brugerapplikationer. I nyere tid har arkitekterne taget et skud på konsistens og taget den endelige konsistensmodel til sig. I denne model afhænger applikationerne af databasestyringssystemet for at flette alle de lokale kopier af dataene for at gøre dem til sidst konsistente.

Alt ser godt ud med den eventuelle konsistensmodel, indtil der er datakonflikter. Et par eventuelle konsistensmodeller lover den bedste indsats for at løse konflikterne, men de mangler at garantere stærk konsistens. Den gode nyhed er, at modellerne bygget omkring konfliktfri replikerede datatyper (CRDT'er) leverer stærk eventuel konsistens.

CRDT'er opnår stærk eventuel konsistens gennem et forudbestemt sæt regler og semantik til konfliktløsning. Applikationer bygget oven på CRDT-baserede databaser skal være designet til at imødekomme konfliktløsningssemantikken. I denne artikel vil vi undersøge, hvordan man designer, udvikler og tester geodistribuerede applikationer ved hjælp af en CRDT-baseret database. Vi vil også undersøge fire eksempler på brugssager: tællere, distribueret caching, delte sessioner og data fra flere regioner.

Min arbejdsgiver, Redis Labs, annoncerede for nylig CRDT-understøttelse i Redis Enterprise med konfliktfri replikerede datatyper, der sluttede sig til den rige portefølje af datastrukturer - Strenge, Hashes, Lists, Sets, Sorted Sets, Bitfields, Geo, Hyperloglog og Streams - i vores databaseprodukt. Følgende diskussion gælder dog ikke kun for Redis Enterprise, men for alle CRDT-baserede databaser.

Databaser til geodistribuerede applikationer

For geodistribuerede applikationer er det almindeligt at køre tjenester lokalt for klienterne. Dette reducerer netværkstrafikken og latenstiden forårsaget af rundtur. I mange tilfælde designer arkitekterne tjenesterne, så de opretter forbindelse til en lokal database. Derefter kommer spørgsmålet om, hvordan du opretholder ensartede data på tværs af alle databaser. En mulighed er at håndtere dette på applikationsniveau - du kan skrive en periodisk jobproces, der synkroniserer alle databaser. Eller du kan stole på en database, der synkroniserer dataene mellem databaserne.

For resten af ​​artiklen antager vi, at du vælger den anden mulighed - lad databasen udføre arbejdet. Som vist i figur 1 nedenfor kører din geodistribuerede applikation tjenester i flere regioner, hvor hver tjeneste opretter forbindelse til en lokal database. Det underliggende databasestyringssystem synkroniserer dataene mellem de databaser, der er distribueret på tværs af regionerne.

Redis Labs

Data konsistens modeller

En konsistensmodel er en kontrakt mellem den distribuerede database og applikationen, der definerer, hvor rene dataene er mellem skrive- og læseoperationer.

For eksempel garanterer databasen i en stærk konsistensmodel, at applikationerne altid læser den sidste skrivning. Med sekventiel konsistens sikrer databasen, at rækkefølgen af ​​data, du læser, er i overensstemmelse med den rækkefølge, som den blev skrevet til databasen. I den eventuelle konsistensmodel lover den distribuerede database at synkronisere og konsolidere dataene mellem databasekopierne bag kulisserne. Derfor, hvis du skriver dine data til en database-replika og læser dem fra en anden, er det muligt, at du ikke læser den seneste kopi af dataene.

Stærk konsistens

Tofaseforpligtelsen er en almindelig teknik for at opnå stærk konsistens. Her, for hver skrivehandling (tilføj, opdater, slet) på en lokal databaseknude, udbreder databaseknudepunktet ændringerne til alle databaseknudepunkterne og venter på, at alle knudepunkterne skal anerkendes. Den lokale knude sender derefter en forpligtelse til alle knudepunkterne og venter på en anden bekræftelse. Ansøgningen kan kun læse dataene efter den anden forpligtelse. Den distribuerede database vil ikke være tilgængelig til skriveoperationer, når netværket afbryder forbindelsen mellem databaserne.

Eventuel konsistens

Den største fordel ved den eventuelle konsistensmodel er, at databasen er tilgængelig for dig til at udføre skriveoperationer, selv når netværksforbindelsen mellem de distribuerede databasekopier går i stykker. Generelt undgår denne model den rundturstid, der opstår i en tofasetilslutning, og understøtter derfor langt flere skriveoperationer pr. Sekund end de andre modeller. Et problem, som eventuel konsistens skal tackle, er konflikter - samtidige skrivninger på det samme emne på to forskellige steder. Baseret på hvordan de undgår eller løser konflikter, klassificeres de til sidst konsistente databaser yderligere i følgende kategorier:

  1. Sidste forfatter vinder (LWW). I denne strategi er de distribuerede databaser afhængige af tidsstemplets synkronisering mellem serverne. Databaserne udveksler tidsstemplet for hver skriveoperation sammen med selve dataene. Skulle der være en konflikt, vinder skriveoperationen med den seneste tidsstempel.

    Ulempen ved denne teknik er, at den antager, at alle systemure er synkroniseret. I praksis er det svært og dyrt at synkronisere alle systemure.

  2. Kvorumbaseret eventuel konsistens: Denne teknik svarer til tofaseforpligtelsen. Den lokale database venter dog ikke på bekræftelse fra alle databaser; det venter bare på anerkendelse fra et flertal af databaser. Anerkendelsen fra flertallet skaber beslutningsdygtighed. Hvis der er en konflikt, vinder den skriveoperation, der har etableret beslutningsdygtigheden.

    På bagsiden tilføjer denne teknik netværkslatens til skriveoperationerne, hvilket gør appen mindre skalerbar. Den lokale database vil heller ikke være tilgængelig for skrivning, hvis den isoleres fra de andre databasekopier i topologien.

  3. Flet replikering: I denne traditionelle tilgang, som er almindelig blandt relationelle databaser, fletter en central fusionsagent alle data. Denne metode giver også en vis fleksibilitet i implementeringen af ​​dine egne regler til løsning af konflikter.

    Fletreplikering er for langsom til at understøtte realtids engagerende applikationer. Det har også et enkelt fejlpunkt. Da denne metode ikke understøtter forudindstillede regler for konfliktløsning, fører det ofte til buggyimplementeringer til konfliktløsning.

  4. Konfliktfri replikeret datatype (CRDT): Du vil lære om CRDT'er i detaljer i de næste par sektioner. I en nøddeskal understøtter CRDT-baserede databaser datatyper og operationer, der leverer konfliktfri eventuel konsistens. CRDT-baserede databaser er tilgængelige, selv når de distribuerede databasekopier ikke kan udveksle dataene. De leverer altid lokal latenstid til læse- og skriveoperationerne.

    Begrænsninger? Ikke alle databaseanvendelsessager drager fordel af CRDT'er. Desuden er konfliktløsningssemantikken for CRDT-baserede databaser foruddefineret og kan ikke tilsidesættes.

Hvad er CRDT'er?

CRDT'er er specielle datatyper, der konvergerer data fra alle databasekopier. De populære CRDT'er er G-tællere (kun vokse-tællere), PN-tællere (positive-negative tællere), registre, G-sæt (kun voksende sæt), 2P-sæt (to-fase sæt), OR-sæt ( observeret-fjern sæt) osv. Bag kulisserne er de afhængige af følgende matematiske egenskaber for at konvergere dataene:

  1. Kommutativ ejendom: a ☆ b = b ☆ a
  2. Associeret ejendom: a ☆ (b ☆ c) = (a ☆ b) ☆ c
  3. Idempotens: a ☆ a = a

En G-tæller er et perfekt eksempel på en operationel CRDT, der fletter operationerne. Her er a + b = b + a og a + (b + c) = (a + b) + c. Kopierne udveksler kun opdateringerne (tilføjelser) med hinanden. CRDT vil flette opdateringerne ved at tilføje dem. Et G-sæt anvender for eksempel idempotens ({a, b, c} U {c} = {a, b, c}) for at flette alle elementerne. Idempotence undgår dobbeltarbejde, der føjes til en datastruktur, når de rejser og konvergerer via forskellige stier.

CRDT-datatyper og deres semantik til konfliktløsning

Konfliktfri datastrukturer: G-tællere, PN-tællere, G-sæt

Alle disse datastrukturer er konfliktfri af design. Tabellerne nedenfor viser, hvordan dataene synkroniseres mellem database replikerne.

Redis Labs Redis Labs

G-tællere og PN-tællere er populære til brugssager som global afstemning, streamtælling, aktivitetssporing og så videre. G-sæt bruges meget til at implementere blockchain-teknologi. Bitcoins anvender for eksempel kun append-blockchain-poster.

Register: Strings, Hashes

Registre er ikke af natur konfliktfri. De følger typisk politikkerne i LWW eller kvorumbaseret konfliktløsning. Figur 4 viser et eksempel på, hvordan et register løser konflikten ved at følge LWW-politikken.

Redis Labs

Register bruges hovedsageligt til lagring af caching- og sessionsdata, brugerprofiloplysninger, produktkatalog osv.

2P-sæt

To-faset sæt opretholder to sæt G-sæt — et til tilføjede emner og det andet til fjernede emner. Kopierne udveksler G-sæt tilføjelser, når de synkroniseres. Konflikt opstår, når det samme element findes i begge sæt. I nogle CRDT-baserede databaser såsom Redis Enterprise håndteres dette af politikken, "Tilføj vinder over sletningen."

Redis Labs

2P-sættet er en god datastruktur til lagring af delte sessionsdata såsom indkøbsvogne, et delt dokument eller et regneark.

Sådan arkiveres et program til brug af en CRDT-baseret database

Forbindelse af din applikation til en CRDT-baseret database er ikke forskellig fra at forbinde din applikation til nogen anden database. På grund af de eventuelle politikker for konsistens skal din applikation dog følge et bestemt sæt regler for at levere en ensartet brugeroplevelse. Tre taster: 

  1. Gør din ansøgning statsløs. En statsløs applikation er typisk API-drevet. Hvert opkald til en API resulterer i at rekonstruere den komplette besked fra bunden. Dette sikrer, at du altid trækker en ren kopi af data til enhver tid. Den lave lokale ventetid, der tilbydes af en CRDT-baseret database, gør rekonstruktion af meddelelser hurtigere og lettere. 

  2. Vælg den rigtige CRDT, der passer til din brugssag. Tælleren er den enkleste af CRDT'erne. Det kan anvendes til brugssager som global afstemning, sporing af aktive sessioner, måling osv. Men hvis du vil flette tilstanden af ​​distribuerede objekter, skal du også overveje andre datastrukturer. For et program, der giver brugerne mulighed for at redigere et delt dokument, kan du f.eks. Ikke kun bevare redigeringerne, men også den rækkefølge, de blev udført i. I så fald ville det være en bedre løsning at gemme redigeringerne på en CRDT-baseret liste eller en kødatastruktur end at gemme dem i et register. Det er også vigtigt, at du forstår konfliktløsningsemantikken håndhævet af CRDT'erne, og at din løsning overholder reglerne.
  3. CRDT er ikke en løsning, der passer til alle. Mens CRDT faktisk er et godt værktøj til mange brugssager, er det måske ikke det bedste for alle brugssager (fx ACID-transaktioner). CRDT-baserede databaser passer generelt godt sammen med mikroservicearkitekturen, hvor du har en dedikeret database til hver mikroservice.

Den vigtigste afhentning her er, at din applikation skal fokusere på logikken og uddelegere datastyring og synkroniseringskompleksitet til den underliggende database.

Test af applikationer med en distribueret multi-master database

For at opnå hurtigere go-to-market anbefaler vi, at du har en konstant opsætning af udvikling, test, iscenesættelse og produktion. Blandt andet betyder det, at din udvikling og testopsætning skal have en miniaturiseret model af din distribuerede database. Kontroller, om din CRDT-baserede database er tilgængelig som en Docker-container eller et virtuelt apparat. Distribuer dine databasekopier på forskellige undernet, så du kan simulere tilsluttet og afbrudt klyngeopsætning.

Test af applikationer med en distribueret multi-master database kan lyde kompliceret. Men det meste af tiden er alt, hvad du vil teste for, datakonsistens og applikationstilgængelighed i to situationer: Når de distribuerede databaser er forbundet, og når der er en netværkspartition mellem databaserne.

Ved at oprette en distribueret database med tre noder i dit udviklingsmiljø kan du dække (og endda automatisere) de fleste testscenarier i enhedstesten. Her er de grundlæggende retningslinjer for test af dine applikationer: