CodeGym /Java kursus /All lectures for DA purposes /Sådan implementeres ACID i en applikation: praksis

Sådan implementeres ACID i en applikation: praksis

All lectures for DA purposes
Niveau , Lektie
Ledig

8.1 Transaktions-id'er

Det er udpeget som XID eller TxID (hvis der er en forskel, fortæl mig det). Tidsstempler kan bruges som TxID, som kan spille i hænderne, hvis vi ønsker at gendanne alle handlinger til et eller andet tidspunkt. Problemet kan opstå, hvis tidsstemplet ikke er granulært nok – så kan transaktioner få samme ID.

Derfor er den mest pålidelige mulighed at generere unikke UUID prod ID'er. I Python er dette meget nemt:

>>> import uuid 
>>> str(uuid.uuid4()) 
'f50ec0b7-f960-400d-91f0-c42a6d44e3d0' 
>>> str(uuid.uuid4()) 
'd15bed89-c0a5-4a72-98d9-5507ea7bc0ba' 

Der er også en mulighed for at hash et sæt transaktionsdefinerende data og bruge denne hash som TxID.

8.2 Forsøg igen

Hvis vi ved, at en bestemt funktion eller et bestemt program er idempotent, betyder det, at vi kan og bør forsøge at gentage dens opkald i tilfælde af en fejl. Og vi skal bare være forberedt på, at en eller anden operation vil give en fejl - i betragtning af at moderne applikationer er fordelt over netværket og hardwaren, skal fejlen ikke betragtes som en undtagelse, men som normen. Fejlen kan opstå på grund af et servernedbrud, netværksfejl, overbelastning af fjernapplikationer. Hvordan skal vores ansøgning opføre sig? Det er rigtigt, prøv at gentage operationen.

Da et stykke kode kan sige mere end en hel side med ord, lad os bruge et eksempel til at forstå, hvordan den naive genforsøgsmekanisme ideelt set burde fungere. Jeg vil demonstrere dette ved hjælp af Tenacity-biblioteket (det er så godt designet, at selvom du ikke planlægger at bruge det, skal eksemplet vise dig, hvordan du kan designe gentagelsesmekanismen):

import logging
import random
import sys
from tenacity import retry, stop_after_attempt, stop_after_delay, wait_exponential, retry_if_exception_type, before_log

logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logger = logging.getLogger(__name__)

@retry(
	stop=(stop_after_delay(10) | stop_after_attempt(5)),
	wait=wait_exponential(multiplier=1, min=4, max=10),
	retry=retry_if_exception_type(IOError),
	before=before_log(logger, logging.DEBUG)
)
def do_something_unreliable():
	if random.randint(0, 10) > 1:
    	raise IOError("Broken sauce, everything is hosed!!!111one")
	else:
    	return "Awesome sauce!"

print(do_something_unreliable.retry.statistics)

> For en sikkerheds skyld vil jeg sige: \@retry(...) er en speciel Python-syntaks kaldet en "decorator". Det er bare en genforsøg(...) funktion, der ombryder en anden funktion og gør noget før eller efter den er udført.

Som vi kan se, kan genforsøg designes kreativt:

  • Du kan begrænse forsøg efter tid (10 sekunder) eller antal forsøg (5).
  • Kan være eksponentiel (det vil sige 2 ** et stigende antal n ). eller på en anden måde (for eksempel fast) for at øge tiden mellem separate forsøg. Den eksponentielle variant kaldes "congestion collaps".
  • Du kan kun prøve igen for visse typer fejl (IOError).
  • Forsøg igen kan forudgås eller afsluttes af nogle specielle poster i loggen.

Nu hvor vi har gennemført young fighter-kurset og kender de grundlæggende byggeklodser, som vi skal bruge for at arbejde med transaktioner på applikationssiden, så lad os stifte bekendtskab med to metoder, der giver os mulighed for at implementere transaktioner i distribuerede systemer.

8.3 Avancerede værktøjer til transaktionselskere

Jeg vil kun give ret generelle definitioner, da dette emne er værdig til en separat stor artikel.

To-faset commit (2 stk) . 2pc har to faser: en forberedelsesfase og en forpligtelsesfase. Under forberedelsesfasen vil alle mikrotjenester blive bedt om at forberede sig på nogle dataændringer, der kan udføres atomært. Når de alle er klar, vil commit-fasen foretage de faktiske ændringer. For at koordinere processen er der brug for en global koordinator, som låser de nødvendige objekter – det vil sige, at de bliver utilgængelige for ændringer, indtil koordinatoren låser dem op. Hvis en bestemt mikrotjeneste ikke er klar til ændringer (f.eks. ikke reagerer), vil koordinatoren afbryde transaktionen og begynde tilbagerulningsprocessen.

Hvorfor er denne protokol god? Det giver atomicitet. Derudover garanterer det isolation ved skrivning og læsning. Det betyder, at ændringer i en transaktion ikke er synlige for andre, før koordinatoren forpligter ændringerne. Men disse egenskaber har også en ulempe: da denne protokol er synkron (blokerende), bremser den systemet (på trods af at selve RPC-kaldet er ret langsomt). Og igen er der fare for gensidig blokering.

Saga . I dette mønster udføres en distribueret transaktion af asynkrone lokale transaktioner på tværs af alle tilknyttede mikrotjenester. Mikrotjenester kommunikerer med hinanden via en eventbus. Hvis en mikrotjeneste ikke fuldfører sin lokale transaktion, vil andre mikrotjenester udføre kompenserende transaktioner for at rulle ændringerne tilbage.

Fordelen ved Saga er, at ingen objekter er blokeret. Men der er selvfølgelig ulemper.

Saga er svær at fejlfinde, især når der er mange mikrotjenester involveret. En anden ulempe ved Saga-mønsteret er, at det mangler læseisolering. Det vil sige, at hvis egenskaberne angivet i ACID er vigtige for os, så er Saga ikke særlig velegnet til os.

Hvad ser vi ud fra beskrivelsen af ​​disse to teknikker? Det faktum, at i distribuerede systemer ligger ansvaret for atomicitet og isolation hos applikationen. Det samme sker ved brug af databaser, der ikke giver ACID-garantier. Det vil sige, at ting som konfliktløsning, rollbacks, commits og frigørelse af plads falder på udviklerens skuldre.

8.4 Hvordan ved jeg, hvornår jeg har brug for SYRE-garantier?

Når der er stor sandsynlighed for, at et bestemt sæt brugere eller processer samtidigt vil arbejde på de samme data .

Beklager banaliteten, men et typisk eksempel er finansielle transaktioner.

Når den rækkefølge, transaktionerne udføres i, har betydning.

Forestil dig, at din virksomhed er ved at skifte fra FunnyYellowChat messenger til FunnyRedChat messenger, fordi FunnyRedChat giver dig mulighed for at sende gifs, men FunnyYellowChat kan ikke. Men du ændrer ikke bare messenger - du migrerer din virksomheds korrespondance fra en messenger til en anden. Det gør du, fordi dine programmører var for dovne til at dokumentere programmer og processer et sted centralt, og i stedet offentliggjorde de alt i forskellige kanaler i messengeren. Ja, og dine sælgere offentliggjorde detaljerne om forhandlinger og aftaler samme sted. Kort sagt, hele dit firmas liv er der, og da ingen har tid til at overføre det hele til en service til dokumentation, og søgningen efter instant messengers fungerer godt, besluttede du i stedet for at rydde murbrokkerne blot at kopiere alle beskeder til en ny placering. Rækkefølgen af ​​beskederne er vigtig

For korrespondance i en messenger er rækkefølgen i øvrigt generelt vigtig, men når to personer skriver noget i samme chat på samme tid, så er det generelt ikke så vigtigt, hvis besked der vises først. Så for dette særlige scenarie ville ACID ikke være nødvendigt.

Et andet muligt eksempel er bioinformatik. Jeg forstår det slet ikke, men jeg antager, at rækkefølgen er vigtig, når man skal dechifrere det menneskelige genom. Jeg hørte dog, at bioinformatikere generelt bruger nogle af deres værktøjer til alt – måske har de deres egne databaser.

Når du ikke kan give en bruger eller behandle forældede data.

Og igen - finansielle transaktioner. For at være ærlig kunne jeg ikke komme i tanke om noget andet eksempel.

Når afventende transaktioner er forbundet med betydelige omkostninger. Forestil dig de problemer, der kan opstå, når en læge og en sygeplejerske både opdaterer en patientjournal og sletter hinandens ændringer på samme tid, fordi databasen ikke kan isolere transaktioner. Sundhedsvæsenet er et andet område, udover finans, hvor ACID-garantier har en tendens til at være kritiske.

8.5 Hvornår har jeg ikke brug for ACID?

Når brugere kun opdaterer nogle af deres private data.

For eksempel efterlader en bruger kommentarer eller noter på en webside. Eller redigerer personlige data på en personlig konto hos en udbyder af tjenester.

Når brugere slet ikke opdaterer data, men kun supplerer med nye (tilføj).

For eksempel en kørende applikation, der gemmer data på dine løbeture: hvor meget du har løbet, til hvilket tidspunkt, rute osv. Hver ny kørsel er nye data, og de gamle redigeres slet ikke. Måske får du, baseret på dataene, analyser - og bare NoSQL-databaser er gode til dette scenarie.

Når forretningslogikken ikke bestemmer behovet for en bestemt rækkefølge, hvori transaktioner udføres.

For en Youtube-blogger, der indsamler donationer til produktion af nyt materiale under den næste live-udsendelse, er det formentlig ikke så vigtigt, hvem, hvornår og i hvilken rækkefølge, der smed ham penge.

Når brugere bliver på den samme webside eller applikationsvindue i flere sekunder eller endda minutter, og derfor vil de på en eller anden måde se forældede data.

Teoretisk set er disse et hvilket som helst online nyhedsmedie eller den samme Youtube. Eller "Habr". Når det ikke betyder noget for dig, at ufuldstændige transaktioner midlertidigt kan gemmes i systemet, kan du ignorere dem uden skader.

Hvis du samler data fra mange kilder, og data, der opdateres med høj frekvens - for eksempel data om belægningen af ​​parkeringspladser i en by, der ændres mindst hvert 5. minut, så vil det i teorien ikke være et stort problem for dig, hvis handlen for en af ​​parkeringspladserne på et tidspunkt ikke går igennem. Selvom det selvfølgelig afhænger af, hvad du præcist vil med disse data.

Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION