MongoDB Academy · Lektion

Ydeevneovervejelser ved transaktioner

De lærende vil måle omkostningerne ved transaktioner på flere dokumenter og designe skemaer, der minimerer behovet for dem i kritiske kodeveje.

Lektion 4 af 413 trin

Ydeevneovervejelser ved transaktioner er en gratis MongoDB Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i MongoDB Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. MongoDB Academy-kurset indeholder 4 lektioner i alt.

Transaktioner har reelle omkostninger

Transaktioner med flere dokumenter i MongoDB giver stærke ACID-garantier, men medfører målbare ydeevneomkostninger. De kræver ekstra netværksrundture til sessionshåndtering, fastholder låse, der blokerer samtidige skrivninger, bruger oplog-plads og kræver koordinering mellem medlemmer af replikasættet for flertallets skrivebekræftelse. Når du forstår disse omkostninger, kan du designe systemer, der kun bruger transaktioner, hvor det er nødvendigt.

Låsekonkurrence og skrivekonflikter

MongoDB-transaktioner bruger låsning på dokumentniveau med optimistisk kontrol af samtidighed. Når en transaktion læser et dokument, tager den et øjebliksbillede, men låser ikke dokumentet. På tidspunktet for commit kontrollerer MongoDB, om en anden skriver har ændret dokumenterne – hvis det er tilfældet, afbrydes transaktionen med en skrivekonflikt. Hyppige konflikter indikerer, at flere transaktioner konkurrerer om de samme ofte ændrede dokumenter, hvilket medfører gentagelser og lavere gennemløb.

// A 'hot document' that many transactions modify simultaneously
// causes frequent WriteConflict errors and retry storms:
db.counters.updateOne({ _id: 'globalOrderCount' }, { $inc: { value: 1 } }, { session });
// Better: use $inc on individual order documents (sharded by orderId),
// or use a dedicated sequence generator outside the transaction.

Omkostninger ved isolering af øjebliksbilleder

Transaktioner læser fra et konsistent øjebliksbillede, der tages ved transaktionens start. Når andre skrivere gennemfører ændringer i transaktionens levetid, skal MongoDB bevare gamle versioner af de ændrede dokumenter (via WiredTigers MVCC-mekanisme), så din transaktion stadig kan se øjebliksbilledet. Langvarige transaktioner får WiredTiger til at opbevare mere versionshistorik i hukommelsen og på disken, hvilket potentielt udløser cachepres, der gør hele klyngen langsommere.

Grænsen på 60 sekunder findes af en god grund

MongoDB afbryder transaktioner, der overskrider 60 sekunder (grænsen kan konfigureres, men bør sjældent øges). En transaktion, der kører i flere minutter, fastholder data fra øjebliksbilledet og forhindrer, at oploggen afkortes. Derfor må langvarige operationer som batchbehandling, komplekse aggregeringer eller ventetid på svar fra eksterne API'er aldrig befinde sig i en transaktion. Hold transaktioner på millisekunder, ikke sekunder.

// WRONG: calling an external API inside a transaction
await session.withTransaction(async () => {
  const order = await db.collection('orders').findOne({ _id: orderId }, { session });
  const result = await externalPaymentAPI.charge(order.amount); // Could take seconds!
  await db.collection('orders').updateOne({ _id: orderId }, { $set: { paid: true } }, { session });
});

// RIGHT: call external API outside the transaction
const result = await externalPaymentAPI.charge(amount); // Do this FIRST
if (result.success) {
  await db.collection('orders').updateOne({ _id: orderId }, { $set: { paid: true, txnId: result.id } });
}

Oplog-størrelsesgrænse: 16 MB pr. transaktion

Hver skrivning i en transaktion registreres i oploggen. MongoDB begrænser den samlede oplog-plads, som en enkelt transaktion kan bruge, til cirka 16 MB. Hvis din transaktion omfatter et stort antal dokumenter eller store dokumenter, kan den overskride denne grænse og blive afbrudt med fejlen TransactionTooLarge. Hvis du skal behandle store batcher, skal du opdele dem i mindre transaktioner med nogle få hundrede dokumenter hver.

// WRONG: inserting 100,000 documents in one transaction
await session.withTransaction(async () => {
  for (const doc of largeArray) { // 100k docs = way over 16MB
    await db.collection('logs').insertOne(doc, { session });
  }
});

// RIGHT: batch into smaller transactions of ~500 docs
const BATCH_SIZE = 500;
for (let i = 0; i < largeArray.length; i += BATCH_SIZE) {
  const batch = largeArray.slice(i, i + BATCH_SIZE);
  await session.withTransaction(async () => {
    await db.collection('logs').insertMany(batch, { session });
  });
}

Skemadesign, der minimerer transaktioner

Den bedste optimering af transaktionsydeevnen er at bruge færre transaktioner. MongoDB's atomaritet på dokumentniveau betyder, at operationer på ét dokument altid overholder ACID. Design dit skema, så operationer, der skal være atomiske, berører så få dokumenter som muligt. Den mest effektive fremgangsmåde er at indlejre relaterede data, der altid opdateres sammen, i ét dokument.

// Without embedding: two documents to update atomically (needs transaction)
await accounts.updateOne({ _id: userId }, { $set: { name: 'Alice' } }, { session });
await profiles.updateOne({ userId: userId }, { $set: { displayName: 'Alice' } }, { session });

// With embedding: one document — no transaction needed
await users.updateOne(
  { _id: userId },
  { $set: { name: 'Alice', 'profile.displayName': 'Alice' } }
  // No session needed — single-document update is atomic

Måling af transaktionsomkostninger

Brug explain('executionStats') og databaseprofileringsværktøjet til at måle det faktiske overhead fra dine transaktioner. Transaktioner vises i loggen over langsomme forespørgsler og i samlingen system.profile med feltet transaction udfyldt. Følg målinger som den gennemsnitlige transaktionsvarighed, antallet af skrivekonflikter og antallet af forsøg pr. transaktionstype. Disse målinger viser, om transaktionernes overhead påvirker din applikations latenstidbudget.

// Enable the profiler to capture slow transactions (threshold: 100ms)
db.setProfilingLevel(1, { slowms: 100 });

// Query the profile for recent slow transactions
db.system.profile.find(
  { 'transaction': { $exists: true } },
  { millis: 1, 'transaction.timingStats': 1, op: 1 }
).sort({ ts: -1 }).limit(10);

Undgå låse, der holdes længe, med findOneAndUpdate

Når du skal kontrollere og ændre ét dokument atomisk, giver findOneAndUpdate ACID-atomaritet uden en transaktion. Den finder atomisk et dokument, der matcher et filter, anvender en opdatering og returnerer enten det gamle eller det nye dokument i én enkelt serveroperation. Dette er det foretrukne mønster til funktioner som test-og-sæt, atomare tællere og overtagelse af elementer fra en kø.

// Atomically claim a pending task — no transaction needed
const task = await db.collection('taskQueue').findOneAndUpdate(
  { status: 'pending' },
  { $set: { status: 'processing', claimedAt: new Date(), workerId: workerId } },
  { returnDocument: 'after', sort: { priority: -1 } }
);

if (!task) {
  console.log('No pending tasks');
}

Mønstret tofaset commit som alternativ

Før MongoDB 4.0 introducerede indbyggede transaktioner, implementerede udviklere manuelt tofaset commit for at opnå atomaritet på tværs af flere dokumenter. I dette mønster holder et centralt 'transaktionsdokument' styr på operationens tilstand (afventer, anvendt, færdig, rollback). Selvom indbyggede transaktioner foretrækkes i dag, viser forståelsen af tofaset commit, hvorfor transaktioner har overhead, og den hjælper i situationer, hvor transaktioner ikke kan bruges (f.eks. sharded clusters i ældre MongoDB-versioner).

// Two-phase commit concept (legacy pattern, prefer native transactions):
// 1. Insert a 'pending' transaction document
// 2. Apply to each document, recording txn ID
// 3. Update transaction to 'committed'
// 4. On failure, query for pending transactions and roll back

Read concern og afvejningen af ydeevne

Transaktioner bruger som standard readConcern: 'snapshot', som giver fuld isolation, men kan være nødt til at vente på, at flertallet af medlemmerne i et replica set bekræfter, at de har modtaget de nyeste data. readConcern: 'local' er hurtigere, men kan læse data, der bliver rullet tilbage ved en failover. For de fleste applikationstransaktioner er 'snapshot' det korrekte valg. Brug kun 'local', hvis du nøje har overvejet konsekvenserne for datakonsistensen.

// 'snapshot' — full isolation, may be slightly slower
session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } });

// 'local' — faster reads, weaker consistency guarantee
session.startTransaction({ readConcern: { level: 'local' }, writeConcern: { w: 'majority' } });

Opsummering: Regler for transaktionsydeevne

Fem regler for transaktioner med høj ydeevne: (1) Hold transaktionerne korte – millisekunder, ikke sekunder. (2) Udfør aldrig I/O uden for databasen inde i en transaktion. (3) Begræns antallet af dokumenter, der berøres pr. transaktion, for at reducere området for mulige konflikter. (4) Brug enkelt-dokument-operationer eller indlejring, når det er muligt, så transaktioner helt kan undgås. (5) Overvåg antallet af skrivekonflikter, og redesign dokumenter med høj belastning, hvis konflikter forekommer ofte.

Hurtigt tjek

Test din forståelse af begreberne i MongoDB & NoSQL Databases fra denne lektion.

Opsummering af lektionen

I denne lektion har du lært, at transaktioner medfører overhead gennem snapshot-isolation, låsekonkurrence og forbrug af plads i oploggen, at du skal holde transaktioner korte (millisekunder) og aldrig udføre langsom I/O i dem, og at den bedste optimering ofte er at redesigne skemaer, så atomaritet på dokumentniveau eliminerer behovet for transaktioner på tværs af flere dokumenter. Som det næste ser vi på change streams til realtidsbaserede hændelsesfeeds fra MongoDB-samlinger.

Gratis at komme i gang

Lær JavaScript med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Ydeevneovervejelser ved transaktioner” gratis?

Ja — hele teksten til “Ydeevneovervejelser ved transaktioner” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af MongoDB Academy-kurset, skal du opgradere til CoddyKit PRO. MongoDB Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Ydeevneovervejelser ved transaktioner”?

De lærende vil måle omkostningerne ved transaktioner på flere dokumenter og designe skemaer, der minimerer behovet for dem i kritiske kodeveje. Du øver dig i MongoDB Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på MongoDB Academy?

Der kræves ingen tidligere erfaring. MongoDB Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “Ydeevneovervejelser ved transaktioner”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne MongoDB Academy-lektion?

Ja. Alle MongoDB Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. ACID-garantier i et distribueret dokumentlager
  2. Start af en session og transaktion på flere dokumenter
  3. Fejlhåndtering og logik til gentagelse
  4. Ydeevneovervejelser ved transaktioner
← Tilbage til MongoDB Academy