Concurrency is a critical aspect of database operations, ensuring multiple clients can read and write data simultaneously without compromising data integrity. MongoDB employs a robust locking and concurrency control system to handle these challenges efficiently. This blog post explores how MongoDB manages locks, explains optimistic and pessimistic locking patterns, and provides monitoring and optimization tips to boost performance in high-concurrency environments.
1. Understanding MongoDB’s Locking Mechanisms
With the WiredTiger storage engine (default since MongoDB 3.2), MongoDB uses document-level locking, allowing concurrent reads and writes at a granular level. However, it still internally tracks lock intents at global, database, and collection levels for coordination. Though developers don’t directly manage locks like in traditional RDBMSs, MongoDB uses internal lock modes for resource coordination:
Types of Locks in MongoDB:
-
Shared (S) Lock:
-
Purpose: Allows multiple clients to read a resource concurrently.
-
Behavior: Coexists with other shared locks but blocks exclusive locks.
-
Example: Multiple clients reading documents from the same collection.
-
-
Exclusive (X) Lock:
-
Purpose: Grants exclusive write access to a resource.
-
Behavior: Prevents any other operation (read or write) on the resource until released.
-
Example: Some DDL operations, such as dropping a collection, take an exclusive lock on the collection. Document updates do not: WiredTiger handles document-level concurrency itself and retries an operation internally when two writes conflict on the same document.
-
-
Intent Shared (IS) Lock:
-
Purpose: Signals the intention to acquire shared locks on subordinate resources.
-
Behavior: Placed at higher levels (e.g., database) when reading collections.
-
Example: Reading a collection places an IS lock on the database.
-
-
Intent Exclusive (IX) Lock:
-
Purpose: Indicates the intention to acquire exclusive locks.
-
Behavior: Applied at higher levels to signal a lower-level exclusive lock.
-
Example: A document update places IX locks on the global, database and collection levels.
-
2. Lock Compatibility Matrix
Understanding lock compatibility is essential for predicting how operations interact.
| Requested Lock | S | X | IS | IX |
|---|---|---|---|---|
| Held Lock | ||||
| S | ✔️ | ❌ | ✔️ | ❌ |
| X | ❌ | ❌ | ❌ | ❌ |
| IS | ✔️ | ❌ | ✔️ | ✔️ |
| IX | ❌ | ❌ | ✔️ | ✔️ |
3. Real-World Implementation of Locking
MongoDB encourages application-level strategies for managing concurrency rather than relying solely on database locks.
Optimistic Locking (Versioning):
Optimistic locking assumes minimal conflict, updating data only if the document version is unchanged.
async function updateDocument(collection, docId, newData) {
const document = await collection.findOne({ _id: docId });
const currentVersion = document.version;
const result = await collection.updateOne(
{ _id: docId, version: currentVersion },
{
$set: { data: newData },
$inc: { version: 1 }
}
);
if (result.modifiedCount === 0) {
throw new Error('Document was modified by another process');
}
}
Pessimistic Locking (Simulated Lock Field):
Pessimistic locking blocks other processes from accessing the resource by setting a lock. The lock must expire, otherwise a crashed process leaves the document locked forever.
async function acquireLock(collection, docId, lockId, ttlMs = 30000) {
const now = new Date();
const result = await collection.updateOne(
{ _id: docId, $or: [{ lock: null }, { lockExpiresAt: { $lt: now } }] },
{ $set: { lock: lockId, lockExpiresAt: new Date(now.getTime() + ttlMs) } }
);
if (result.modifiedCount === 0) {
throw new Error('Document is already locked');
}
}
async function releaseLock(collection, docId, lockId) {
await collection.updateOne(
{ _id: docId, lock: lockId },
{ $set: { lock: null, lockExpiresAt: null } }
);
}
4. Monitoring Locks and Diagnosing Issues
MongoDB provides lock monitoring through the serverStatus command.
// View current lock statistics
db.serverStatus().locks
-
Deadlocks: Monitor
deadlockCountto identify deadlocks. -
Performance Metrics: Review
timeAcquiringMicrosandacquireWaitCountto assess lock contention.
db.currentOp({ active: true, waitingForLock: true }) //check active lock waits
5. Deadlock Prevention Techniques
-
Ordered Operations: Update documents in a consistent order across transactions.
-
Timeouts: Limit how long an operation may run using
maxTimeMS; the server stops the operation when the limit is reached. It is an operation time limit, not a lock-duration setting.
// Limit update time
db.collection.updateOne(
{ _id: ObjectId("507f191e810c19729de860ea") },
{ $set: { status: "active" } },
{ maxTimeMS: 1000 }
)
- Retry Logic for Transactions: The driver’s
withTransaction()already retriesTransientTransactionErrorand unknown commit results for a bounded time, so use it on its own and keep the callback idempotent:
async function runTransaction(client) {
const session = client.startSession();
try {
await session.withTransaction(async () => {
// Transaction logic here; pass { session } to every operation
});
} finally {
await session.endSession();
}
}
6. Lock Optimization Strategies
-
MongoDB Lock Optimization Techniques: Understand how to minimize lock contention by optimizing query patterns and using proper indexing.
-
Field-Level Updates: Minimize lock duration by updating only necessary fields.
-
Use of Secondary Indexes: Reduce the number of documents scanned during queries.
7. Comparison: Optimistic vs. Pessimistic Locking
| Feature | Optimistic Locking | Pessimistic Locking |
|---|---|---|
| Best For | High-read environments | High-write environments |
| Concurrency | High | Low |
| Complexity | Medium | High |
| Performance Impact | Minimal | Can degrade under contention |
8. Key Takeaways
-
Monitor lock metrics regularly to avoid performance degradation.
-
Implement versioning or lock fields to manage concurrency.
-
Use
withTransaction()so transient transaction errors and write conflicts are retried.
By mastering MongoDB’s locking and concurrency mechanisms, developers can ensure data integrity while maximizing performance in high-concurrency environments. Happy coding!
Production notes (October 2026)
These notes add the diagnostic queries, decision criteria and validation steps the topic needs on a production system.
Do not treat every slow write as a collection lock
MongoDB's WiredTiger engine uses document-level concurrency control for writes and intent locks at broader levels. Many operations on different documents can run concurrently. A slow or retried write may come from a hot document, a long transaction, resource pressure or an application retry pattern; a simplistic lock-mode table will not identify which one.
Write conflicts and lock waits are different signals. WiredTiger can detect a conflict between operations and retry one; application-level transactions may need explicit retry handling. An operation waiting for a lock should be investigated alongside its command, duration and competing activity.
Separate lock waits from write conflicts
A lock wait means an operation is waiting for a lock mode to become available. A write conflict means concurrent work touched the same document or snapshot and one operation must retry. Neither signal alone proves that a collection is globally serialized. Correlate the event with the affected command, document access pattern, transaction duration and CPU or storage pressure.
Evidence to collect during the event
- Identify the slow endpoint or job, affected collection, time window and whether latency is in reads, writes or transaction commits.
- Inspect current operations and lock statistics, using the deployment's supported tooling and permissions. Compare with CPU, cache pressure, disk activity and transaction metrics.
- Look for repeated updates to the same document, long-running transactions, large batch operations and retries that multiply traffic.
- Change one cause at a time, then compare throughput, error/retry rate and tail latency over similar traffic.
On a deployment that permits it, the $currentOp aggregation stage can show active operations, waitingForLock and per-operation lock statistics. Scope the query to the incident window and follow your deployment's privilege and observability rules. MongoDB recommends this stage over the older currentOp command.
db.getSiblingDB('admin').aggregate([
{ $currentOp: { allUsers: true, idleConnections: false } },
{ $match: { waitingForLock: true } },
{ $project: { opid: 1, op: 1, ns: 1, secs_running: 1,
waitingForLock: 1, lockStats: 1 } }
]);For a hot-document pattern, first reduce unnecessary updates or shorten the transaction that owns the document. For a broad throughput drop, compare cache, disk and application retry rate before redesigning locking. A version field can protect an application-level update from overwriting another change, but it does not replace a diagnosis of server contention.
MongoDB's WiredTiger documentation describes document-level concurrency and transparent retries. Its concurrency FAQ explains lock visibility and monitoring options.
The current consulting practice is PostgreSQL-first; other database work is scoped individually.