En guide til forståelse af databasestørrelsesmønstre

Der er mange artikler online, der beskriver databasens skalerbarhedsmønstre, men de er for det meste spredte artikler - bare teknikker, der defineres tilfældigt uden meget sammenhæng. Jeg finder ud af, at de ikke er defineret trin for trin og ikke diskuterer, hvornår man skal vælge hvilken skaleringsindstilling, hvilke skaleringsindstillinger der er mulige i praksis, og hvorfor.

Derfor planlægger jeg at diskutere nogle af teknikkerne detaljeret i fremtidige artikler. For at starte, føler jeg det er bedre, hvis jeg trin for trin diskuterer teknikker med en eller anden kontekst på min egen måde. Denne artikel er en artikel på højt niveau - Jeg vil ikke diskutere skaleringsteknikker i detaljer her, men vil give et overblik. Så lad os komme i gang.

En casestudie

Antag at du har bygget en opstart, der tilbyder deling af køreture til en billig pris. Oprindeligt når du starter, målretter du mod en by og har næppe flere snesevis af kunder efter din første annonce.

Du gemmer alle kunder, rejser, placeringer, bookingsdata og kunderejshistorik i den samme database eller sandsynligvis i en enkelt fysisk maskine. Der er ingen fancy caching eller big data pipeline til at løse problemer, da din app er meget ny. Dette er perfekt til din brugssag i øjeblikket, da der er meget få kunder, og dit system næppe f.eks. Booker en tur på 5 minutter.

Men som tiden går, begynder flere mennesker at tilmelde sig dit system, da du er den billigste service på markedet og takket være din promovering og annoncer. Du begynder at booke, siger 10 bookinger pr. Minut, og langsomt stiger antallet til 20, 30 bookinger pr. Minut.

På dette tidspunkt indser du, at systemet er begyndt at fungere dårligt: ​​API-latenstid er steget meget, og nogle transaktioner er blokeret eller sulter, og til sidst mislykkes de. Din app tager mere tid på at reagere og forårsager kundetilfredshed. Hvad kan du gøre for at løse problemet?

Mønster 1 - Forespørgsel Optimering & Connection Pool implementering:

Den første løsning, der kommer i tankerne, er, at cachen ofte bruger ikke-dynamiske data som bookinghistorik, betalingshistorik, brugerprofiler og så videre. Men efter denne caching af applikationslag kan du ikke løse latensproblemet med API'er, der udsætter dynamiske data som den aktuelle driverplacering eller de nærmeste førerhuse for en given kunde eller aktuelle rejseomkostninger på et bestemt tidspunkt efter turens start.

Du identificerer, at din database sandsynligvis er stærkt normaliseret, så du introducerer nogle overflødige kolonner (disse kolonner vises ofte i WHEREeller JOIN ONklausul i forespørgsler) i meget anvendte tabeller af denormaliseringens skyld. Dette reducerer joinforespørgsler, opdeler en stor forespørgsel i flere mindre forespørgsler og tilføjer deres resultater op i applikationslaget.

En anden parallel optimering, som du kan gøre, er at finjustere omkring databaseforbindelser. Databaseklientbiblioteker og eksterne biblioteker er tilgængelige på næsten alle programmeringssprog. Du kan bruge forbindelsespuljebiblioteker til at cache databaseforbindelser eller kan konfigurere forbindelsespuljestørrelse i selve databasestyringssystemet.

Oprettelse af enhver netværksforbindelse er dyr, da det kræver noget frem og tilbage kommunikation mellem klient og server. Pooling-forbindelser kan hjælpe dig med at optimere antallet af forbindelser. Forbindelsespoolbiblioteker kan hjælpe dig med multiplexforbindelser - flere applikationstråde kan bruge den samme databaseforbindelse. Jeg skal se, om jeg senere kan forklare pooling af forbindelser i detaljer i en separat artikel.

Din mål latenstid for dine API'er og find sandsynligvis 20–50% eller mere reduceret latenstid. Dette er god optimering på dette tidspunkt.

Du har nu skaleret din virksomhed til en by mere, mere kundetilmelding, du begynder langsomt at foretage 80-100 bookinger pr. Minut. Dit system er ikke i stand til at håndtere denne skala. Igen ser du, at API-latens er steget, databaselaget har givet op, men denne gang giver ingen forespørgseloptimering dig nogen væsentlig præstationsgevinst. Du kontrollerer systemets metric, du finder diskplads næsten fuld, CPU har travlt 80% af tiden, RAM fyldes meget hurtigt op.

Mønster 2 - Lodret skalering eller opskalering:

Efter at have undersøgt alle systemmetrics, ved du, at der ikke er nogen anden nem løsning snarere end at opgradere systemets hardware. Du opgraderer din RAM-størrelse 2 gange, opgraderer diskplads med f.eks. 3 gange eller mere. Dette kaldes lodret skalering eller skalering af dit system. Du informerer dit infrastrukturteam eller devops-team eller tredjepartsdatacenteragenter om at opgradere din maskine.

Men hvordan indstiller du maskinen til vertikal skalering?

Du tildeler en større maskine. En fremgangsmåde er ikke at migrere data manuelt fra gammel maskine, men indstil den nye maskine replicatil den eksisterende maskine ( primary) - lav en midlertidig primary replicakonfiguration. Lad replikeringen ske naturligt. Når replikeringen er færdig, skal du promovere den nye maskine til primær og tage den ældre maskine offline. Da den større maskine forventes at betjene alle anmodninger, sker al læsning / skrivning på denne maskine.

Fedt nok. Dit system er i gang igen med øget ydeevne.

Din virksomhed klarer sig meget godt, og du beslutter dig for at skalere til yderligere 3 byer - du er nu operationel i 5 byer i alt. Trafikken er 3 gange gange tidligere, det forventes, at du foretager omkring 300 bookinger pr. Minut. Inden du overhovedet opnår denne målreservation, rammer du igen performance crunch, databaseindeksets størrelse øges kraftigt i hukommelsen, den har brug for konstant vedligeholdelse, bordscanning med index bliver langsommere end nogensinde. Du beregner omkostningerne ved at opskalere maskinen yderligere, men ikke overbevist om prisen. Hvad gør du nu?

Mønster 3 - Segmentering af ansvar for kommandoforespørgsel (CQRS):

Du identificerer, at den store maskine ikke er i stand til at håndtere alle read/writeanmodninger. Også i de fleste tilfælde har enhver virksomhed brug for transaktionsfunktioner på, writemen ikke på readoperationer. Du har også det godt med lidt inkonsekvent eller forsinket readdrift, og din virksomhed har heller ikke noget problem med det. Du ser en mulighed, hvor det kan være en god mulighed at adskille fysisk og maskinmæssigt read& writeoperationer. Det vil skabe plads til, at individuelle maskiner kan håndtere flere read/writeoperationer.

Du tager nu yderligere to store maskiner og indstiller dem replicatil den aktuelle maskine. Database replikering tager sig af distribution af data fra primarymaskine til replicamaskiner. Du navigerer alle læseforespørgsler (Forespørgsel ( Q) ind CQRS) til replikerne - enhver replicakan tjene enhver læseanmodning, du navigerer alle skriveforespørgsler (Kommando ( C) ind CQRS) til primary. Der kan være lidt forsinkelse i replikeringen, men ifølge din forretningsbrug er det fint.

De fleste af mellemstore startups, der serverer nogle få hundrede tusind anmodninger hver dag, kan overleve med primær-replika-opsætning, forudsat at de regelmæssigt arkiverer ældre data.

Nu skalerer du til 2 flere byer, og du ser, at din primaryikke er i stand til at håndtere alle writeanmodninger. Mange writeanmodninger har forsinkelse. Desuden er forsinkelsen mellem primaryog replicaundertiden påvirket kunder og chauffører ex - når turen slutter, betaler kunden chaufføren med succes, men chaufføren kan ikke se betalingen, da kundens aktivitet er en writeanmodning, der går til primary, mens chaufførens aktivitet er en readanmodning der går til en af ​​replikerne. Dit overordnede system er så langsomt, at chaufføren ikke kan se betalingen i mindst et halvt minut - frustrerende for både chauffør og kunde. Hvordan løser du det?

Mønster 4 - Multi Primær replikering

Du skaleres rigtig godt med primary-replicakonfiguration, men nu har du brug for mere skriveydelse. Du er muligvis klar til at gå på kompromis med en smule ved readanmodningens ydeevne. Hvorfor ikke distribuere skriveanmodningen til en replicaogså?

I en multi-primarykonfiguration kan alle maskiner fungere som begge primary& replica. Du kan tænke på multi-primarysom en cirkel af maskiner siger A->B->C->D->A. Bkan replikere data fra A, Ckan replikere data fra B, Dkan replikere data fra C, Akan replikere data fra D. Du kan skrive data til en hvilken som helst knude, mens du læser data, kan du sende forespørgslen til alle noder, hvem svarer, returnerer det. Alle noder vil have samme databaseskema, samme sæt tabeller, indeks osv. Så du skal sørge for, at der ikke er nogen kollision på idtværs af noder i den samme tabel, ellers ville flere noder under udsendelse returnere forskellige data for det samme id.

Generelt er det bedre at bruge UUIDeller GUIDtil id. En yderligere ulempe ved denne teknik er - readforespørgsler kan være ineffektive, da det involverer udsendelsesforespørgsel og få det rigtige resultat - dybest set scatter collect-tilgang.

Nu skalerer du til yderligere 5 byer, og dit system har smerter igen. Det forventes, at du håndterer ca. 50 anmodninger pr. Sekund. Du har et desperat behov for at håndtere et stort antal samtidige anmodninger. Hvordan opnår du det?

Mønster 5 - opdeling:

Du ved, at din locationdatabase er noget, der bliver høj writeog readtrafik. Sandsynligvis er write:readforholdet 7:3. Dette lægger et stort pres på de eksisterende databaser. De locationtabeller indeholder få primære data som longitude, latitude, timestamp, driver id, trip idetc. Det har ikke en meget at gøre med brugernes ture, brugerdata, betalingsdata etc. Hvad med at adskille de locationtabeller i en separat database skema? Hvad med at placere den database i separate maskiner med korrekt primary-replicaeller multi-primarykonfiguration?

Dette kaldes partitionering af data efter funktionalitet. Forskellig database kan være vært for data kategoriseret efter forskellige funktioner, hvis det kan kræves, kan resultatet aggregeres i bagenden. Ved hjælp af denne teknik kan du fokusere på at skalere de funktionaliteter, der kræver høje read/writeanmodninger. Selvom bagenden eller applikationslaget skal tage ansvaret for at slutte sig til resultaterne, når det er nødvendigt, hvilket sandsynligvis resulterer i flere kodeændringer.

Forestil dig nu, at du har udvidet din virksomhed til i alt 20 byer i dit land og planlægger at udvide til Australien snart. Dit stigende behov for app kræver hurtigere og hurtigere respons. Ingen af ​​ovenstående metoder kan hjælpe dig til det ekstreme nu. Du skal skalere dit system på en sådan måde, at udvidelse til andre lande / regioner ikke altid har brug for dig til at foretage hyppige tekniske eller arkitektoniske ændringer. Hvordan gør du det?

Mønster 6 - vandret skalering:

Du googler meget, læser meget om, hvordan andre virksomheder har løst problemet - og kommer til den konklusion, at du skal skalere vandret. Du tildeler siger 50 maskiner - alle har det samme databaseskema, som igen indeholder det samme sæt tabeller. Alle maskiner har bare en del af data.

Da alle databaser indeholder det samme sæt tabeller, kan du designe systemet på en sådan måde, at datalokaliteten er der, dvs. alle relaterede data lander i den samme maskine. Hver maskine kan have deres egne replikaer, replikaer kan bruges til gendannelse af fejl. Hver af databaser kaldes shard. En fysisk maskine kan have en eller flere shards- det er op til dit design, hvordan du vil. Du skal beslutte på en sharding keysådan måde, at en enkelt sharding keyaltid refererer til den samme maskine. Så du kan forestille dig mange maskiner, der alle har relaterede data i samme sæt tabeller, read/writeanmodninger om samme række eller samme sæt ressourcejord i den samme databasemaskine.

Sharding er generelt hårdt - i det mindste siger ingeniører fra forskellige virksomheder. Men når du betjener millioner eller milliarder af anmodninger, skal du tage en så hård beslutning.

Jeg vil diskutere shardingmere detaljeret i mit næste indlæg, så jeg holder på min fristelse til at diskutere mere i dette indlæg.

Nu da du har sharding på plads, er du sikker på, at du kan skalere til mange lande. Din virksomhed er vokset så meget, at investorer skubber dig til at skalere virksomheden på tværs af kontinenter. Du ser igen nogle problemer her. API-latenstid igen. Din service er hostet i USA, og folk fra Vietnam har svært ved at booke forlystelser. Hvorfor? Hvad gør du ved det?

Mønster 7 - Data Center Wise Partition:

Din virksomhed vokser i Amerika, Sydasien og i få lande i Europa. Du foretager millioner af bookinger dagligt med milliarder af anmodninger, der rammer din server. Tillykke - dette er et højdepunkt for din virksomhed.

Men da anmodninger fra appen skal rejse på tværs af kontinenter gennem hundreder eller tusinder af servere på internettet, opstår latenstiden. Hvad med at distribuere trafik på tværs af datacentre? Du kan oprette et datacenter i Singapore, der håndterer alle anmodninger fra Sydasien, datacenter i Tyskland kan håndtere alle anmodninger fra europæiske lande, og et datacenter i Californien kan håndtere alle USA-anmodninger.

Du aktiverer også replikering på tværs af datacenter, som hjælper med at gendanne katastrofer. Så hvis Californiens datacenter replikerer til Singapore datacenter, hvis Californiens datacenter går ned på grund af elektricitetsproblemer eller naturlig ulykke, kan alle USA-anmodninger falde tilbage til Singapore datacenter og så videre.

Denne skaleringsteknik er nyttig, når du har millioner af kunder til at betjene på tværs af lande, og du ikke kan imødekomme tab af data, du skal altid opretholde tilgængeligheden af ​​systemet.

Dette er nogle generelle trin for trin teknikker til databaseskalering. Selvom de fleste ingeniører ikke får nok chance for at implementere disse teknikker, men som helhed er det bedre at få en bredere idé om et sådant system, som i fremtiden måske kan hjælpe dig med at udføre bedre system- og arkitekturdesign.

I mine næste artikler vil jeg forsøge at diskutere nogle af begreberne detaljeret. Du er velkommen til at give passende feedback til dette indlæg, hvis nogen.

Artiklen blev oprindeligt offentliggjort på forfatterens mediumkonto: //medium.com/@kousiknath/understanding-database-scaling-patterns-ac24e5223522