A concise guide to the two fundamental strategies for managing concurrent access to shared data in databases and distributed systems, explaining when to use each and the trade-offs involved.
When multiple transactions or processes read and write shared data simultaneously, conflicts can corrupt state or produce inconsistent results. Locking strategies are mechanisms that control how and when access to a resource is granted or deferred. The two dominant strategies are optimistic locking and pessimistic locking, each making different assumptions about how likely conflicts are to occur.
Pessimistic locking assumes conflicts are likely, so it acquires an exclusive lock on a resource before reading or modifying it, blocking all other accessors until the lock is released. A classic example is a SQL SELECT FOR UPDATE statement, which holds a row-level lock for the duration of the transaction. This guarantees data integrity but reduces throughput, increases latency, and risks deadlocks if two transactions wait on each other's locks.
Optimistic locking assumes conflicts are rare, so it does not lock the resource upfront. Instead, it reads the data along with a version number or timestamp, performs work, and at write time checks whether the version has changed since it was read. If the version matches, the update succeeds and the version is incremented; if it has changed, the transaction is aborted and typically retried. This approach maximizes concurrency but places the burden of conflict handling on the application layer.
In a relational database, optimistic locking is often implemented by adding a version or updated_at column to a table. The UPDATE statement includes a WHERE clause checking that the column still matches the value read earlier, e.g., UPDATE orders SET status='shipped', version=6 WHERE id=1 AND version=5. If zero rows are affected, the application knows a conflict occurred and must decide whether to retry or surface an error to the user.
Use pessimistic locking when contention is high, operations are long-running, or the cost of a retry is prohibitive — for example, financial ledger updates or inventory reservation systems. Use optimistic locking when reads vastly outnumber writes, contention is low, and retries are cheap — such as user profile updates or document edits. Mixing strategies incorrectly is a common mistake: applying optimistic locking to a high-contention resource causes a retry storm that degrades performance worse than a simple lock would.
Deadlocks are a critical risk with pessimistic locking; always acquire locks in a consistent order and set a lock timeout to avoid indefinite blocking. With optimistic locking, ensure retries are bounded — implement an exponential backoff with a maximum retry count to prevent infinite loops under sustained contention. Never expose a raw version column as a public API field without sanitizing it, as clients could spoof version values and bypass conflict detection entirely.
© RM Full Stack & AI Engineer · All guides · Roadmaps · Open the app