ML
Database

Pessimistic vs Optimistic Locking: Which One You Actually Need

Two strategies for concurrent writes. Pick wrong and you either serialise all your traffic or lose data quietly.

July 29, 20258 min readDatabaseConcurrency

Two users open the same record, both hit Save. Whose change wins, and does the other one know? That's the concurrency problem — and the database gives you two styles of answer.

Pessimistic locking

Acquire a row-level lock before you touch the data. No one else can modify that row until you commit.

BEGIN;
SELECT * FROM orders WHERE id = 42 FOR UPDATE;
-- row is exclusively locked until COMMIT / ROLLBACK
UPDATE orders SET status = 'shipped' WHERE id = 42;
COMMIT;

Use when: contention is high, the update is short, and correctness trumps throughput. Classic examples: seat reservations, inventory decrement, money transfers.

Watch out for: long-held locks (they queue everyone else), deadlocks (two tx grab rows in opposite order), and connection-pool starvation.

Optimistic locking

No lock. Every row carries a version number. On update, you check the version hasn't changed and bump it. If it has — retry.

-- Read
SELECT id, total, version FROM orders WHERE id = 42;
-- version = 7

-- Write (fails if someone else updated in between)
UPDATE orders
   SET total = 199, version = version + 1
 WHERE id = 42 AND version = 7;

-- rows_affected = 0 → conflict; re-read and retry

Use when: contention is low and you want high throughput. Most web apps (edit a blog post, update a profile) fit here. Hibernate and Entity Framework both have @Version annotations that automate this.

Side-by-side

PessimisticOptimistic
ThroughputLowerHigher
Contention behaviourQueuesRetries
Deadlock riskYesNo
Data loss riskNoneNone (retries surface conflict)
Best forMoney, inventoryUser-edited content

Hybrid: advisory locks

Sometimes you want a short coordination primitive outside the row model — "only one worker processes this job at a time." Postgres advisory locks (pg_try_advisory_lock) and Redis locks (single-flight) shine here.

The rule

Start with optimistic locking and @Version everywhere you have user-editable data. Reach for pessimistic locks in the specific spots where a lost retry is worse than a brief queue. Don't mix the two on the same table without a very good reason.

SharePostLinkedIn

Reader Discussion

6 replies// weighed in

TopNewestAuthor
Add to the thread
Disagree, agree harder, or share your own experience…
Email instead →markdown okbe kind
  1. Highlighted by author
    Victor Petrov· Senior BackendAgrees

    "removing indexes is the third-year move" — saving this. first year you add, second year you tune, third year you realise half of them have been dead weight slowing every write since that one feature got cut.

    Jul 31, 2025·2 days later
  2. Greta Schäfer· Senior EngineerAgrees

    isolation levels are the part juniors skip and seniors keep underestimating. read committed feels safe; it's not — phantom reads still exist. the moment money is involved, default to repeatable read and benchmark.

    Aug 05, 2025·1 week later
  3. Aya Fujimoto· Database EngineerPushback

    small note on Postgres covering indexes — INCLUDE columns don't get the same treatment as key columns for HOT updates and dedup. people assume index-only scan == free, but the heap visibility map can still send you back to disk. measure first.

    Aug 02, 2025·4 days later
  4. Dan O'Connor· Eng ManagerStory

    N+1 cost us a P1 incident last Black Friday. ONE endpoint was firing 3,400 queries per cart load. Looked fine in dev (3 carts), looked fine in staging (50 carts), production hit 9k QPS to the DB and cardiac arrest. Add load tests.

    Aug 01, 2025·3 days later
  5. Linh Phạm· Java DeveloperAgrees

    @Version optimistic locking is the 80/20 of CRUD apps. genuinely think JPA should make it default-on with a @NoVersion opt-out. the number of last-write-wins races I've debugged in spring boot apps that didn't set it is too damn high

    Aug 03, 2025·5 days later
  6. Isabella Costa· Junior EngineerKind words

    saved this. sharing at standup tomorrow — we've had exactly this problem for 2 sprints and nobody on the team had framed it this way 🙏

    Jul 31, 2025·2 days later

Worked on something similar? Email ducminhldm@gmail.com — I read every one. The good ones become future posts.

Comments seeded · live discussion via email