Back to the map

Module 18 · Transactions and concurrency · Lesson 4 of 5

Timestamp ordering and optimistic concurrency

Concurrency control without waiting for locks: timestamp ordering with read and write timestamps, the Thomas write rule, optimistic concurrency's read, validate and write phases.

DeepShould-knowSenior 13 min Updated Nov 2026

Why it matters

Locking makes transactions wait. Two other families of protocols avoid waiting by deciding the serial order in advance (timestamp ordering) or by checking for conflicts only at the end (optimistic concurrency control). They appear in exams alongside 2PL, inside real systems (validation-based engines, distributed databases, in-memory stores), and, in their simplest form, in nearly every web application as "optimistic locking" with a version column.

The idea

Timestamp ordering

Each transaction gets a unique timestamp when it starts, TS(Ti), and the protocol forces the schedule to be equivalent to running transactions in timestamp order. Each data item X records the largest timestamp of any transaction that has read it, R-TS(X), and that has written it, W-TS(X).

Operation by TiReject (abort and restart Ti) ifOtherwise
Read XTS(Ti) < W-TS(X): a younger transaction already wrote X, so Ti would read a value from its own futureRead; set R-TS(X) = max(R-TS(X), TS(Ti))
Write XTS(Ti) < R-TS(X): a younger transaction already read the old X, so Ti's write is too lateContinue checks below
Write XTS(Ti) < W-TS(X): a younger transaction already wrote XAbort (basic protocol), or ignore the write (Thomas write rule)
Write X (all checks pass)Write; set W-TS(X) = TS(Ti)

Transactions never wait, so there are no deadlocks; instead, transactions that arrive "too late" are aborted and restarted with a new timestamp. The Thomas write rule improves throughput: if a younger transaction already wrote X and nobody read Ti's would-be value in between, Ti's write is obsolete and can simply be skipped. That admits some view-serializable schedules that are not conflict serializable (the blind-write case from the serializability lesson). Basic timestamp ordering can produce unrecoverable schedules unless commits are delayed until the transactions read from have committed, so practical versions add that rule.

Optimistic concurrency control

Optimistic concurrency control (OCC) assumes conflicts are rare. Each transaction runs in three phases.

  1. 1

    Read phase

    Execute the transaction, reading from the database and buffering all writes privately. Record the read set and write set.
  2. 2

    Validation phase

    At commit, check whether any transaction that committed while this one ran wrote something this one read (or, depending on the scheme, overlaps with its write set). If so, abort and restart.
  3. 3

    Write phase

    If validation passes, apply the buffered writes to the database. Validation and write happen atomically, typically in a short critical section.
T1read phase (private writes)validatewrite ✓T2read phase: read X (T1 then writes X)validate✕T2 read X before T1's committed write → T2 aborts and retries
OCC runs without locks, buffering writes; conflicts are discovered at validation, where the losing transaction aborts and retries.

Optimistic locking in applications

The same idea appears at application level: add a version column, read the row with its version, and update with a condition on it.

UPDATE accounts
SET balance = 450, version = version + 1
WHERE id = 42 AND version = 7;
-- 0 rows updated means someone else changed it: reload and retry, or report a conflict

It holds no locks while a user edits a form for minutes, which pessimistic locking (SELECT FOR UPDATE) cannot afford. HTTP's ETag with If-Match, from the System Design REST lesson, is the same pattern over an API.

Choosing among the families

2PL (pessimistic)Timestamp orderingOptimistic (OCC)
Conflict handlingWait for locksAbort transactions that arrive too lateAbort at validation
DeadlocksPossibleNoneNone
Best whenContention is high and transactions are shortClocks or counters are cheap; read-mostlyConflicts are rare; long or interactive work
Cost under contentionWaiting, deadlock retriesMany restartsWasted work from aborted transactions

Trade-offs

Pessimistic locking wastes time waiting when conflicts are rare; optimistic schemes waste whole transactions when conflicts are common, and can starve long transactions that keep losing validation. Timestamp ordering avoids deadlocks by design at the cost of restarts and timestamp bookkeeping on every item. Most production databases combine approaches: multiversioning for reads, locks for writes, and optimistic version checks in the application for long-running edits.

In an interview

Pick by contention: "Booking the last seat in a flash sale is high contention on one row, so I lock it briefly with SELECT FOR UPDATE inside a short transaction. Editing a product description in an admin form is low contention and slow, so I use a version column and reject stale saves with a conflict message." Then mention that OCC and timestamp schemes cannot deadlock.

Check yourself

Under basic timestamp ordering, transaction T (timestamp 10) wants to write X, and R-TS(X) is 15. What happens and why?

Timestamp ordering and optimistic concurrency · Backplane