0Pricing
MongoDB Academy · 강의

트랜잭션 성능 고려 사항

학습자는 다중 문서 트랜잭션의 오버헤드를 측정하고, 자주 실행되는 경로에서 트랜잭션의 필요성을 최소화하는 스키마를 설계합니다.

트랜잭션 성능 고려 사항은(는) CoddyKit의 무료 MongoDB Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 MongoDB Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. MongoDB Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

Transactions Have Real Overhead

Multi-document transactions in MongoDB provide powerful ACID guarantees but come with measurable performance overhead. They involve additional network round-trips for session management, hold locks that block concurrent writers, consume oplog space, and require coordination across replica set members for the majority write concern. Understanding this overhead helps you design systems that use transactions only where necessary.

Lock Contention and Write Conflicts

MongoDB transactions use document-level locking with optimistic concurrency control. When a transaction reads a document, it takes a snapshot but does not lock it. At commit time, MongoDB checks if any other writer modified those documents—if so, the transaction aborts with a write conflict. Frequent conflicts indicate that multiple transactions are competing for the same hot documents, causing retries and degraded throughput.

// 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.

Snapshot Isolation Cost

Transactions read from a consistent snapshot taken at transaction start time. As other writers commit during your transaction's lifetime, MongoDB must maintain old versions of modified documents (through the WiredTiger MVCC mechanism) so your transaction can still see the snapshot. Long-running transactions cause WiredTiger to hold more version history in memory and on disk, potentially triggering cache pressure that slows down the entire cluster.

The 60-Second Limit Exists for Good Reason

MongoDB aborts transactions that exceed 60 seconds (configurable, but rarely should be increased). A transaction that runs for minutes holds snapshot data and prevents the oplog from being truncated. This is why long-running operations like batch processing, complex aggregations, or waiting for external API responses must never be inside a transaction. Keep transactions to milliseconds, not seconds.

// 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 Size Limit: 16 MB Per Transaction

Each write in a transaction is recorded in the oplog. MongoDB limits the total oplog space a single transaction can consume to approximately 16 MB. If your transaction involves a large number of documents or large documents, it can exceed this limit and abort with a TransactionTooLarge error. If you need to process large batches, break them into smaller transactions of a few hundred documents each.

// 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 });
  });
}

Schema Design to Minimize Transactions

The best performance optimization for transactions is to use fewer of them. MongoDB's single-document atomicity means that operations on one document are always ACID-compliant. Design your schema so that operations that must be atomic touch as few documents as possible. The most effective approach is embedding related data that is always updated together into a single document.

// 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

Measuring Transaction Overhead

Use explain('executionStats') and the database profiler to measure the actual overhead of your transactions. Transactions appear in the slow query log and system.profile collection with their transaction field populated. Track metrics like average transaction duration, write conflict rate, and number of retries per transaction type. These metrics reveal whether transaction overhead is impacting your application's latency budget.

// 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);

Avoiding Long-Held Locks With findOneAndUpdate

When you need to atomically check and modify a single document, findOneAndUpdate provides ACID atomicity without a transaction. It atomically finds a document matching a filter, applies an update, and returns either the old or new document in a single server-side operation. This is the preferred pattern for patterns like test-and-set, atomic counters, and claiming queue items.

// 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');
}

Two-Phase Commit Pattern as Alternative

Before MongoDB 4.0 introduced native transactions, developers implemented two-phase commit manually to achieve multi-document atomicity. In this pattern, a central 'transaction document' tracks the state of the operation (pending, applied, done, rollback). While native transactions are preferred today, understanding two-phase commit reveals why transaction overhead exists and helps in scenarios where transactions cannot be used (e.g., sharded clusters in older MongoDB versions).

// 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 and Its Performance Trade-off

Transactions default to readConcern: 'snapshot' which provides full isolation but may need to wait for the majority of replica set members to confirm they have received the latest data. readConcern: 'local' is faster but may read data that gets rolled back in a failover. For most application transactions, 'snapshot' is correct. Only use 'local' if you have carefully considered the consistency implications.

// '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' } });

Summary: Transaction Performance Rules

Five rules for high-performance transaction usage: (1) Keep transactions short—milliseconds not seconds. (2) Never perform I/O outside the database inside a transaction. (3) Minimize the number of documents touched per transaction to reduce conflict surface area. (4) Use single-document operations or embedding whenever possible to avoid transactions entirely. (5) Monitor write conflict rates and redesign hot documents if conflicts are frequent.

Quick Check

Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.

Lesson Recap

In this lesson you learned: transactions add overhead through snapshot isolation, lock contention, and oplog space consumption, keep transactions short (milliseconds) and never perform slow I/O inside them, and the best optimization is often to redesign schemas so that single-document atomicity eliminates the need for multi-document transactions. Next up we explore change streams for real-time event feeds from MongoDB collections.

자주 묻는 질문

“트랜잭션 성능 고려 사항” 강의는 무료인가요?

네 — “트랜잭션 성능 고려 사항” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 MongoDB Academy 강의 전체를 잠금 해제할 수 있습니다. MongoDB Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“트랜잭션 성능 고려 사항”에서 뭘 배우나요?

학습자는 다중 문서 트랜잭션의 오버헤드를 측정하고, 자주 실행되는 경로에서 트랜잭션의 필요성을 최소화하는 스키마를 설계합니다. 브라우저에서 직접 실행하는 실습 코드로 MongoDB Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

MongoDB Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 MongoDB Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“트랜잭션 성능 고려 사항” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 MongoDB Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 MongoDB Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 분산 문서 저장소의 ACID 보장
  2. 세션 및 다중 문서 트랜잭션 시작하기
  3. 오류 처리 및 재시도 로직
  4. 트랜잭션 성능 고려 사항
← MongoDB Academy(으)로 돌아가기