Cosmos DB是否支持资源锁或键范围锁?如何实现串行写入控制?
Absolutely, you can solve this concurrency problem in Cosmos DB—let’s walk through the most practical approaches that fit your needs, whether you need to ensure strict serial execution of update operations or lock at the partition level while still letting reads happen unblocked:
1. Optimistic Concurrency Control (OCC) with ETags
This is Cosmos DB’s built-in, go-to approach for most concurrency scenarios. Here’s how it works for your increment use case:
- When you need to increment the value, first read the latest version of the document and capture its
ETag(a unique string that identifies the document’s current state). - When writing the incremented value back (using
ReplaceItemAsyncorUpsertItemAsync), include theIfMatchEtagoption with the ETag you captured. - If another operation modified the document in between, Cosmos DB will return a
412 Precondition Failedresponse. Your client code can then retry the entire read-modify-write cycle (read the new latest version, increment, write again).
This guarantees that only the operation with the most up-to-date document version succeeds, so you’ll end up with 13 and 14 instead of duplicate 13s. Best of all, reads are never blocked—Cosmos DB uses multi-version concurrency control (MVCC), so reads will always return a consistent snapshot of the data even while writes are in flight.
2. Server-Side Stored Procedures
Cosmos DB stored procedures execute atomically within a single partition, and requests targeting the same partition key are processed serially. This is perfect for your scenario because:
- You can wrap the entire "read current value → increment → write new value" logic inside a stored procedure.
- Concurrent requests to the same partition will be queued and run one after another, eliminating any chance of race conditions.
- Reads from the partition still work normally while the stored procedure runs (thanks to MVCC), so you don’t have to block read access.
Here’s a quick example of what the stored procedure logic might look like (in JavaScript):
function incrementDocumentValue(docId) { const context = getContext(); const collection = context.getCollection(); // Read the current document collection.readDocument(`${collection.getSelfLink()}/docs/${docId}`, (err, doc) => { if (err) throw err; // Increment the value field doc.value += 1; // Write the updated document back collection.replaceDocument(doc._self, doc, (err) => { if (err) throw err; context.getResponse().setBody(doc); }); }); }
Your client only needs to call this stored procedure, and Cosmos DB handles the serial execution for the partition automatically.
3. Simulated Pessimistic Locking (Partition-Level)
If you need explicit lock-like behavior (though this is less common than the above approaches), you can simulate a pessimistic lock using a dedicated "lock document" in the same partition:
- Create a small lock document in the target partition (e.g.,
{ "id": "partition-lock", "isLocked": false }). - When an operation needs to run, first try to acquire the lock by replacing the lock document with
isLocked: true, using ETag validation to make sure no other operation has locked it. - If the replace succeeds, perform your read-modify-write increment.
- Once done, release the lock by setting
isLocked: falseagain. - If lock acquisition fails (another operation holds it), your client can wait and retry.
⚠️ Heads up: You’ll need to handle lock timeouts to avoid deadlocks (e.g., if an operation crashes without releasing the lock). This adds extra complexity, so only use this if OCC or stored procedures don’t fit your specific needs.
Which Approach Should You Pick?
- OCC is great for simplicity and performance, especially if conflicts are rare. It’s the default choice for most increment scenarios.
- Stored Procedures are ideal if you want to offload concurrency handling to the server, or if your increment logic is more complex than just adding 1.
- Simulated Pessimistic Locking is only necessary if you require strict, explicit serial execution before any read operation starts.
All of these approaches let reads continue unblocked, which aligns with your "allow reading" requirement for partition-level locking.
内容的提问来源于stack exchange,提问作者Ilya Chernomordik

