Programmering

Når skal jeg bruke en CRDT-basert database

Roshan Kumar er senior produktsjef i Redis Labs.

Å bøye konsistensen og tilgjengeligheten som beskrevet av CAP-setningen har vært en stor utfordring for arkitektene av geodistribuerte applikasjoner. Nettverkspartisjon er uunngåelig. Den høye ventetiden mellom datasentre resulterer alltid i noe frakobling mellom datasentrene i en kort periode. Dermed er tradisjonelle arkitekturer for geodistribuerte applikasjoner designet for enten å gi opp datakonsistensen eller slå et slag på tilgjengeligheten.

Dessverre har du ikke råd til å ofre tilgjengeligheten for interaktive brukerapplikasjoner. I nyere tid har arkitektene tatt et skudd på konsistens og omfavnet den endelige konsistensmodellen. I denne modellen er applikasjonene avhengig av databasestyringssystemet for å slå sammen alle lokale kopier av dataene for å gjøre dem til slutt konsistente.

Alt ser bra ut med den endelige konsistensmodellen til det er datakonflikter. Noen få endelige konsistensmodeller lover best mulig innsats for å løse konfliktene, men mangler å garantere sterk konsistens. Den gode nyheten er at modellene bygget rundt konfliktfrie replikerte datatyper (CRDT) gir sterk sluttkonsistens.

CRDT oppnår sterk eventuell konsistens gjennom et forhåndsbestemt sett med konfliktløsningsregler og semantikk. Programmer bygget på toppen av CRDT-baserte databaser må være utformet for å imøtekomme konfliktløsningsemantikken. I denne artikkelen vil vi undersøke hvordan du kan designe, utvikle og teste geodistribuerte applikasjoner ved hjelp av en CRDT-basert database. Vi vil også undersøke fire eksempler på brukstilfeller: tellere, distribuert caching, delte økter og data fra flere regioner.

Arbeidsgiveren min, Redis Labs, kunngjorde nylig CRDT-støtte i Redis Enterprise, med konfliktfrie replikerte datatyper som blir med i den rike porteføljen av datastrukturer - Strenger, Hashes, Lists, Sets, Sorted Sets, Bitfields, Geo, Hyperloglog og Streams - i vårt databaseprodukt. Følgende diskusjon gjelder imidlertid ikke bare Redis Enterprise, men alle CRDT-baserte databaser.

Databaser for geodistribuerte applikasjoner

For geodistribuerte applikasjoner er det vanlig å kjøre tjenester lokalt for kundene. Dette reduserer nettverkstrafikken og ventetiden forårsaket av tur-retur. I mange tilfeller designer arkitektene tjenestene for å koble til en lokal database. Så kommer spørsmålet om hvordan du opprettholder konsistente data på tvers av alle databasene. Et alternativ er å håndtere dette på applikasjonsnivå - du kan skrive en periodisk jobbprosess som vil synkronisere alle databasene. Eller du kan stole på en database som vil synkronisere dataene mellom databasene.

For resten av artikkelen antar vi at du vil gå med det andre alternativet - la databasen gjøre jobben. Som vist i figur 1 nedenfor, kjører det geodistribuerte programmet tjenester i flere regioner, hvor hver tjeneste kobles til en lokal database. Det underliggende databasesystemet synkroniserer dataene mellom databasene distribuert over regionene.

Redis Labs

Datakonsistensmodeller

En konsistensmodell er en kontrakt mellom den distribuerte databasen og applikasjonen som definerer hvor rene dataene er mellom skrive- og leseoperasjoner.

For eksempel, i en sterk konsistensmodell, garanterer databasen at applikasjonene alltid vil lese den siste skrivingen. Med sekvensiell konsistens, forsikrer databasen at rekkefølgen på dataene du leser er i samsvar med rekkefølgen den ble skrevet til databasen. I den endelige konsistensmodellen lover den distribuerte databasen å synkronisere og konsolidere dataene mellom databasekopiene bak kulissene. Derfor, hvis du skriver dataene dine til en databasekopi og leser dem fra en annen, er det mulig at du ikke leser den siste kopien av dataene.

Sterk konsistens

To-fase forpliktelsen er en vanlig teknikk for å oppnå sterk konsistens. Her, for hver skriveoperasjon (legg til, oppdater, slett) på en lokal databasenode, forplanter databasenoden endringene i alle databasenodene og venter på at alle nodene skal bekreftes. Den lokale noden sender deretter en forpliktelse til alle nodene og venter på en annen bekreftelse. Søknaden vil kunne lese dataene først etter den andre forpliktelsen. Den distribuerte databasen vil ikke være tilgjengelig for skriveoperasjoner når nettverket kobler fra databasene.

Eventuell konsistens

Den største fordelen med den endelige konsistensmodellen er at databasen vil være tilgjengelig for deg for å utføre skriveoperasjoner selv når nettverkstilkoblingen mellom de distribuerte databasekopiene brytes. Generelt unngår denne modellen den rundturstiden som påføres en tofasetakst, og støtter derfor langt flere skriveoperasjoner per sekund enn de andre modellene. Et problem som eventuell konsistens må adressere, er konflikter - samtidig skriver om det samme elementet på to forskjellige steder. Basert på hvordan de unngår eller løser konflikter, blir de til slutt konsistente databasene ytterligere klassifisert i følgende kategorier:

  1. Siste forfatter vinner (LWW). I denne strategien er de distribuerte databasene avhengige av tidsstempelsynkroniseringen mellom serverne. Databasene utveksler tidsstempelet for hver skriveoperasjon sammen med selve dataene. Skulle det være en konflikt, vinner skriveoperasjonen med siste tidsstempel.

    Ulempen med denne teknikken er at den antar at alle systemklokkene er synkronisert. I praksis er det vanskelig og kostbart å synkronisere alle systemklokkene.

  2. Kvorumsbasert eventuell konsistens: Denne teknikken ligner på to-fase forpliktelsen. Den lokale databasen venter imidlertid ikke på bekreftelsen fra alle databasene. det bare venter på bekreftelse fra et flertall av databasene. Bekreftelsen fra flertallet oppretter et beslutningsdyktig antall. Skulle det være en konflikt, vinner skriveoperasjonen som har etablert beslutningsdyktigheten.

    På baksiden legger denne teknikken til nettverksforsinkelse i skriveoperasjonene, noe som gjør appen mindre skalerbar. Den lokale databasen vil heller ikke være tilgjengelig for skriving hvis den blir isolert fra de andre databasekopiene i topologien.

  3. Slå sammen replikering: I denne tradisjonelle tilnærmingen, som er vanlig blant relasjonsdatabasene, smelter en sentralisert sammenslåingsagent alle dataene. Denne metoden gir også litt fleksibilitet i å implementere dine egne regler for å løse konflikter.

    Flette replikering er for treg til å støtte sanntids engasjerende applikasjoner. Det har også et enkelt feilpunkt. Siden denne metoden ikke støtter forhåndsinnstilte regler for konfliktløsning, fører det ofte til buggyimplementeringer for konfliktløsning.

  4. Konfliktfri replikert datatype (CRDT): Du vil lære om CRDT i detalj i de neste par seksjonene. I et nøtteskall støtter CRDT-baserte databaser datatyper og operasjoner som gir konfliktfri eventuell konsistens. CRDT-baserte databaser er tilgjengelige selv når de distribuerte databasekopiene ikke kan utveksle dataene. De leverer alltid lokal latens til lese- og skriveoperasjonene.

    Begrensninger? Ikke alle databasebrukssaker har fordeler av CRDT. Dessuten er konfliktløsningsemantikken for CRDT-baserte databaser forhåndsdefinert og kan ikke overstyres.

Hva er CRDT?

CRDT er spesielle datatyper som konvergerer data fra alle databasekopier. De populære CRDT-ene er G-tellere (bare voksende tellere), PN-tellere (positive-negative tellere), registre, G-sett (bare voksende sett), 2P-sett (to-fasesett), OR-sett ( observerte-fjerne sett) osv. Bak kulissene stoler de på følgende matematiske egenskaper for å konvergere dataene:

  1. Kommutativ eiendom: a ☆ b = b ☆ a
  2. Assosiativ eiendom: a ☆ (b ☆ c) = (a ☆ b) ☆ c
  3. Idempotens: a ☆ a = a

En G-teller er et perfekt eksempel på en operativ CRDT som slår sammen operasjonene. Her er a + b = b + a og a + (b + c) = (a + b) + c. Kopiene utveksler bare oppdateringene (tilleggene) med hverandre. CRDT vil slå sammen oppdateringene ved å legge dem sammen. Et G-sett bruker for eksempel egenmakt ({a, b, c} U {c} = {a, b, c}) for å slå sammen alle elementene. Idempotence unngår duplisering av elementer lagt til en datastruktur når de reiser og konvergerer via forskjellige baner.

CRDT datatyper og deres semantikk for konfliktløsning

Konfliktfrie datastrukturer: G-tellere, PN-tellere, G-sett

Alle disse datastrukturene er konfliktfrie av design. Tabellene nedenfor viser hvordan dataene synkroniseres mellom databasekopiene.

Redis Labs Redis Labs

G-tellere og PN-tellere er populære for brukstilfeller som global avstemning, strømtelling, aktivitetssporing og så videre. G-sett brukes mye til å implementere blockchain-teknologi. Bitcoins, for eksempel, bruker bare appel-blockchain-oppføringer.

Registers: Strings, Hashes

Register er ikke konfliktfrie av natur. De følger vanligvis retningslinjene for LWW eller quorumbasert konfliktløsning. Figur 4 viser et eksempel på hvordan et register løser konflikten ved å følge LWW-policyen.

Redis Labs

Register blir hovedsakelig brukt til å lagre caching og øktdata, brukerprofilinformasjon, produktkatalog, etc.

2P-sett

To-fase sett opprettholder to sett med G-sett — det ene for tilføyde gjenstander og det andre for fjernede gjenstander. Kopiene utveksler G-set-tillegg når de synkroniseres. Konflikt oppstår når det samme elementet finnes i begge settene. I noen CRDT-baserte databaser som Redis Enterprise håndteres dette av policyen, "Add wins over the delete."

Redis Labs

2P-settet er en god datastruktur for lagring av delte sesjonsdata som handlekurver, et delt dokument eller et regneark.

Hvordan lage et program for å bruke en CRDT-basert database

Å koble applikasjonen til en CRDT-basert database er ikke forskjellig fra å koble applikasjonen til noen annen database. På grunn av den endelige konsistenspolitikken, må søknaden din imidlertid følge et bestemt sett med regler for å gi en jevn brukeropplevelse. Tre taster: 

  1. Gjør søknaden statsløs. En statsløs applikasjon er vanligvis API-drevet. Hver samtale til et API resulterer i å rekonstruere hele meldingen fra bunnen av. Dette sikrer at du alltid trekker en ren kopi av data når som helst. Den lave lokale ventetiden som tilbys av en CRDT-basert database gjør rekonstruering av meldinger raskere og enklere. 

  2. Velg riktig CRDT som passer din brukstilfelle. Telleren er den enkleste av CRDT-ene. Den kan brukes til brukstilfeller som global stemmegivning, sporing av aktive økter, måling osv. Men hvis du vil slå sammen tilstanden til distribuerte objekter, må du også vurdere andre datastrukturer. For et program som lar brukerne redigere et delt dokument, kan det være lurt å ikke bare bevare endringene, men også rekkefølgen de ble utført i. I så fall vil lagring av endringene i en CRDT-basert liste eller en kødatastruktur være en bedre løsning enn å lagre dem i et register. Det er også viktig at du forstår konfliktløsningsemantikken håndhevet av CRDT-ene, og at løsningen din overholder reglene.
  3. CRDT er ikke en løsning som passer alle. Mens CRDT virkelig er et flott verktøy for mange brukstilfeller, er det kanskje ikke det beste for alle brukssaker (for eksempel ACID-transaksjoner). CRDT-baserte databaser passer vanligvis godt med mikrotjenestearkitektur der du har en dedikert database for hver mikrotjeneste.

Hovedtaket her er at søknaden din skal fokusere på logikken og delegere kompleksiteten til datastyring og synkronisering til den underliggende databasen.

Testing av applikasjoner med en distribuert multimaster-database

For å oppnå raskere markedsføring anbefaler vi at du har en jevn utvikling, testing, iscenesettelse og produksjonsoppsett. Blant annet betyr det at utviklings- og testoppsettet ditt må ha en miniatyrisert modell av den distribuerte databasen din. Sjekk om din CRDT-baserte database er tilgjengelig som en Docker-container eller et virtuelt apparat. Distribuer databasekopiene dine på forskjellige delnett slik at du kan simulere tilkoblet og frakoblet klyngeoppsett.

Test av applikasjoner med en distribuert multi-master database kan høres komplisert ut. Men det meste av tiden alt du vil teste for er datakonsistens og applikasjonstilgjengelighet i to situasjoner: Når de distribuerte databasene er koblet til, og når det er en nettverkspartisjon mellom databasene.

Ved å sette opp en distribuert database med tre noder i utviklingsmiljøet ditt, kan du dekke (og til og med automatisere) de fleste testscenariene i enhetstestingen. Her er de grunnleggende retningslinjene for testing av applikasjonene dine: