Does MongoDB support multi-document Transactions?

MongoDB: Atomicitet og Transaktioner Forklaret

23/09/2022

Rating: 4.87 (8752 votes)

I en verden af databaser er begrebet atomicitet fundamentalt. Det garanterer, at en operation enten fuldføres helt eller slet ikke – en alt-eller-intet-præmis, der sikrer dataintegritet. Men hvordan håndterer en fleksibel, dokument-orienteret database som MongoDB dette princip? Svaret er nuanceret og afgørende for enhver udvikler, der arbejder med platformen. Mens MongoDB tilbyder stærke garantier på niveauet for et enkelt dokument, åbner operationer på tværs af flere dokumenter op for en række overvejelser, som vi vil dykke ned i her.

What are CRUD operations in MongoDB?
MongoDB CRUD Operations Insert Documents Insert Methods Query Documents Query on Embedded/Nested Documents Query an Array Query an Array of Embedded Documents Project Fields to Return from Query Query for Null or Missing Fields Iterate a Cursor in the mongoShell Update Documents Update Methods Delete Documents Delete Methods Bulk Write Operations
Indholdsfortegnelse

Atomicitet på Enkelt-Dokument Niveau: MongoDB's Kernegaranti

Kernen i MongoDB's design er, at enhver skriveoperation på et enkelt dokument er atomar. Dette gælder, selvom operationen ændrer flere indlejrede felter i det pågældende dokument. Denne simple, men kraftfulde garanti er ofte alt, hvad der er nødvendigt for et væld af applikationer, især når man udnytter MongoDB's styrke: den denormaliserede datamodel.

Forestil dig et system til et bibliotek, hvor vi skal holde styr på bøger, antallet af tilgængelige eksemplarer og hvem der har lånt dem. I en relationel database ville disse oplysninger måske være spredt over tre separate tabeller. I MongoDB kan vi elegant indlejre dem i et enkelt dokument:

{ "_id": 123456789, "title": "MongoDB: The Definitive Guide", "author": ["Kristina Chodorow", "Mike Dirolf"], "available": 3, "checkout": [ { "by": "joe", "date": ISODate("2012-10-15") } ] }

Når en ny bruger, 'abc', låner en bog, skal vi gøre to ting samtidigt: reducere antallet af tilgængelige eksemplarer (`available`) med én og tilføje brugerens oplysninger til `checkout`-arrayet. Takket være enkelt-dokument atomicitet kan vi gøre dette i én enkelt, sikker operation:

db.books.updateOne( { _id: 123456789, available: { $gt: 0 } }, { $inc: { available: -1 }, $push: { checkout: { by: "abc", date: new Date() } } } )

Denne operation er atomar. Det er umuligt for en anden proces at se bogen i en mellemtilstand, hvor `available` er blevet reduceret, men `checkout`-arrayet endnu ikke er opdateret. Enten sker begge ændringer, eller ingen af dem sker. Dette eliminerer en hel klasse af potentielle data-inkonsistenser og forenkler applikationslogikken markant.

Udfordringer med Samtidige Opdateringer

Selvom enkelt-dokument atomicitet er stærk, kan der opstå problemer, når flere operationer forsøger at opdatere det samme dokument samtidigt. Resultatet afhænger i høj grad af, hvordan dine forespørgsler er struktureret.

Lad os overveje et dokument med en `score` på 80. To separate processer forsøger at opdatere den:

  • Proces A: Opdaterer `score` til 90, men kun hvis den nuværende `score` er 80.
  • Proces B: Opdaterer `score` til 100, men kun hvis den nuværende `score` er 80.

Her vil kun én af operationerne lykkes. Hvis Proces A kører først, ændres `score` til 90. Når Proces B derefter forsøger at køre, matcher dens filter (`{ score: 80 }`) ikke længere, og opdateringen udføres ikke. Dette er en sikker og forudsigelig adfærd.

Men hvad nu hvis filteret er på et andet felt, som ikke ændres?

  • Proces A: Opdaterer `score` til 90 baseret på dokumentets `_id`.
  • Proces B: Opdaterer `score` til 100 baseret på det samme `_id`.

I dette scenarie vil begge operationer matche. Hvis Proces A kører og sætter `score` til 90, og Proces B kører umiddelbart efter, vil den overskrive værdien og sætte `score` til 100. Dette er et klassisk "lost update"-problem, hvor Proces A's ændring går tabt, uden at klienten, der sendte den, får besked.

Løsninger på Samtidighedsproblemer

Heldigvis tilbyder MongoDB værktøjer til at håndtere disse situationer elegant og sikkert.

Is a write operation atomic in MongoDB?
In MongoDB, a write operation is atomic on the level of a single document, even if the operation modifies multiple values. When multiple update commands happen in parallel, each individual command ensures that the query condition still matches.

Brug af Atomare Operatører

For at undgå "lost updates", især med numeriske data, bør man bruge atomare operatører som `$inc` (increment). Lad os tage et nyt eksempel, hvor vi vil lægge henholdsvis 10 og 20 til en `score` på 80.

  • Proces A: `db.scores.updateOne({ _id: 1 }, { $inc: { score: 10 } })`
  • Proces B: `db.scores.updateOne({ _id: 1 }, { $inc: { score: 20 } })`

Her overskriver operationerne ikke hinanden. `$inc` læser den nuværende værdi, lægger tallet til og gemmer resultatet. Uanset rækkefølgen vil den endelige `score` korrekt blive 110 (80 + 10 + 20). Operatører som `$inc`, `$mul`, `$push` og `$pull` er designet til at modificere data baseret på den nuværende tilstand, hvilket gør dem ideelle til samtidige operationer.

Sikring af Unikke Værdier

En anden vigtig del af dataintegritet er at sikre, at visse felter har unikke værdier (f.eks. en brugers e-mailadresse). Dette håndteres ikke via skriveoperationer, men på skemaniveau med unikke indekser. Et unikt indeks forhindrer indsættelse eller opdatering af dokumenter, hvis det ville skabe en duplikatværdi i det indekserede felt. Man kan også oprette et sammensat unikt indeks på tværs af flere felter for at sikre, at kombinationen af værdier er unik.

Håndtering af Flere Dokumenter: Multi-Document Transactions

Hvad sker der, når en enkelt logisk handling kræver ændringer i flere dokumenter? En operation som `db.collection.updateMany()` ændrer flere dokumenter, men operationen som helhed er ikke atomar. Ændringen af hvert enkelt dokument er atomar, men en anden proces kan potentielt læse data i en tilstand, hvor nogle dokumenter er opdateret, men andre ikke er.

For situationer, der kræver "alt-eller-intet"-garantier på tværs af flere dokumenter (og endda flere collections), understøtter moderne versioner af MongoDB multi-dokument transaktioner. Dette er en game-changer, der bringer traditionelle ACID-garantier til dokumentmodellen.

En transaktion giver dig mulighed for at gruppere en række læse- og skriveoperationer. Hele gruppen bliver derefter enten 'committet' (anvendt permanent) eller 'aborted' (rullet helt tilbage), som om ingen af operationerne nogensinde havde fundet sted. Dette er essentielt for handlinger som en bankoverførsel, hvor penge skal trækkes fra én konto (ét dokument) og indsættes på en anden (et andet dokument). Uden en transaktion kunne en fejl midtvejs efterlade databasen i en inkonsistent tilstand.

Is a write operation atomic in MongoDB?
In MongoDB, a write operation is atomic on the level of a single document, even if the operation modifies multiple values. When multiple update commands happen in parallel, each individual command ensures that the query condition still matches.

Det er dog vigtigt at bemærke, at selv med tilgængeligheden af transaktioner, er den denormaliserede datamodel (at indlejre data i et enkelt dokument) ofte stadig den mest optimale og performante løsning. Transaktioner medfører et performance-overhead og bør kun bruges, når datamodellen absolut kræver det.

Sammenligning af Tilgange

Her er en tabel, der sammenligner de to primære metoder til at opnå atomicitet i MongoDB:

EgenskabEnkelt-Dokument OpdateringMulti-Dokument Transaktion
OmfangÉt enkelt dokument.Flere dokumenter, på tværs af flere collections.
YdeevneMeget høj. Standard operation.Lavere. Medfører overhead pga. låsning og koordination.
KompleksitetLav. Indbygget i databasen.Højere. Kræver eksplicit sessions- og transaktionshåndtering i koden.
AnvendelsesscenarieOpdatering af relaterede data, der kan indlejres (f.eks. produktinfo og lagerstatus).Operationer, hvor relaterede data logisk bor i separate dokumenter (f.eks. bankoverførsel).

Ofte Stillede Spørgsmål (FAQ)

Er alle skriveoperationer i MongoDB atomare?

Nej. Kun operationer, der modificerer et enkelt dokument, er atomare som standard. Operationer, der påvirker flere dokumenter, som `updateMany`, er ikke atomare som helhed, medmindre de pakkes ind i en eksplicit multi-dokument transaktion.

Hvornår skal jeg bruge en multi-dokument transaktion i stedet for at indlejre data?

Brug indlejring (denormalisering) når det er muligt. Det er hurtigere og enklere. Vælg multi-dokument transaktioner, når dataene ikke logisk kan eller bør eksistere i det samme dokument. Et godt eksempel er relationer mellem to ligeværdige entiteter, som f.eks. to bankkonti, der tilhører forskellige kunder.

Påvirker transaktioner ydeevnen?

Ja. Transaktioner kræver, at databasen holder på flere låse over længere tid for at sikre isolation. Dette kan reducere den samtidige gennemstrømning af systemet. Derfor er det en afvejning mellem stærk konsistens og maksimal ydeevne. Brug dem, når dataintegriteten kræver det, men ikke som standard for alle operationer.

Hvad med ældre metoder som Two-Phase Commit?

Før indbyggede multi-dokument transaktioner blev introduceret, implementerede udviklere mønstre som 'Two-Phase Commit' på applikationsniveau for at simulere transaktioner. Disse mønstre er komplekse at implementere korrekt og er i dag stort set overflødige takket være den indbyggede transaktionssupport, som er mere robust og lettere at bruge.

Hvis du vil læse andre artikler, der ligner MongoDB: Atomicitet og Transaktioner Forklaret, kan du besøge kategorien Teknologi.

Go up