Concurrency Control Problems in Database Systems

Concurrency Control Problems in Database Systems

Verified Sources
Sep 11, 2026

Concurrency control is the set of techniques a DBMS uses to coordinate simultaneous transactions while preserving correctness. Without control, interleaved reads and writes can produce anomalies even when every individual transaction is correct.

This section explains three fundamental problems:

  1. Lost update — one transaction overwrites another transaction’s change.
  2. Dirty read — a transaction reads data written by another transaction before it commits.
  3. Unrepeatable read — a transaction reads the same row twice and obtains different committed values.

These problems are consequences of insufficient isolation between transactions. The SQL standard defines isolation levels that trade consistency for concurrency and performance.

A useful correctness goal is serializability: although transactions may execute concurrently, the final result should be equivalent to running them one at a time.

Footnotes

  1. Transaction Isolation Levels (ODBC) - Microsoft Learn - Describes the four SQL-92 isolation levels and the read phenomena they may permit.

Transaction Isolation Levels and Concurrency Anomalies

Transactions and schedules

Suppose two transactions, T1T_1 and T2T_2, access the same data item XX.

  • R1(X)R_1(X) means that T1T_1 reads XX.
  • W1(X)W_1(X) means that T1T_1 writes XX.
  • C1C_1 means that T1T_1 commits.
  • A1A_1 means that T1T_1 aborts or rolls back.

A schedule describes the actual execution order. A serial schedule completes one transaction before starting another:

R1(X),W1(X),C1,R2(X),W2(X),C2R_1(X), W_1(X), C_1, R_2(X), W_2(X), C_2

A non-serial schedule interleaves operations:

R1(X),R2(X),W1(X),W2(X),C1,C2R_1(X), R_2(X), W_1(X), W_2(X), C_1, C_2

Non-serial execution can improve throughput, but the DBMS must ensure that the interleaving does not violate application rules or database constraints.

Key terms

  • Atomicity
  • Consistency
  • Commit
  • Rollback

Footnotes

  1. Chapter 18: Concurrency Control - Database System Concepts - Academic material covering schedules, serializability, locking, validation, and multiversion control.

How to Analyze a Concurrent Schedule

  1. 1
    Step 1

    Label each operation according to its transaction, such as T1T_1 and T2T_2.

  2. 2
    Step 2

    Record which data items each transaction reads or modifies. Conflicts occur when operations from different transactions access the same item and at least one operation is a write.

  3. 3
    Step 3

    Determine whether a transaction has committed before another transaction reads or depends on its changes.

  4. 4
    Step 4

    If one transaction reads the same row twice, compare the values. A changed value indicates a possible unrepeatable read.

  5. 5
    Step 5

    If two transactions calculate updates from the same old value, inspect whether the later write overwrites the earlier one. This indicates a possible lost update.

  6. 6
    Step 6

    Use an appropriate isolation level, row lock, version check, or serializable transaction to prevent the anomaly.

1. Lost Update

A lost update occurs when two transactions read the same original value, independently calculate new values, and then write those values back. The later write replaces the earlier write, so one logical update disappears.

Example

Assume an account balance is initially 100100.

TimeTransaction T1T_1Transaction T2T_2Balance
1Reads balance 100100100100
2Reads balance 100100100100
3Calculates 100+20=120100 + 20 = 120100100
4Calculates 10030=70100 - 30 = 70100100
5Writes 120120120120
6Writes 70707070

The correct result, assuming both operations should be applied, is:

100+2030=90100 + 20 - 30 = 90

However, the final value is 7070. The deposit made by T1T_1 has been lost.

Schedule

R1(B),R2(B),W1(B=120),W2(B=70),C1,C2R_1(B), R_2(B), W_1(B{=}120), W_2(B{=}70), C_1, C_2

The problem is not merely that the writes overlap. Each transaction computed its result from a stale copy of the balance.

Common causes

  • Read-modify-write logic performed in separate application steps.
  • Missing row-level locks.
  • An isolation level or storage engine that permits conflicting writes.
  • Updating an entire row from an old object representation.
  • Omitting a version or timestamp condition in an UPDATE statement.

Prevention

A database can prevent lost updates by:

  • Locking the row before reading it for modification.
  • Performing the calculation inside an atomic SQL update.
  • Using optimistic concurrency control with a version column.
  • Using a sufficiently strong isolation level.
  • Detecting a conflict and retrying the transaction.

An atomic update is often safer than reading a value into application code:

UPDATE accounts SET balance = balance + 20 WHERE account_id = 1\texttt{UPDATE accounts SET balance = balance + 20 WHERE account\_id = 1}

Here, the database evaluates the expression as part of the update operation rather than allowing application code to overwrite a value read earlier.

Lost Update Timeline

  1. 1
    Step 1

    Both transactions read X=100X=100.

  2. 2
    Step 2

    T1T_1 computes 120120, while T2T_2 computes 7070.

  3. 3
    Step 3

    T1T_1 writes 120120.

  4. 4
    Step 4

    T2T_2 writes 7070, replacing the value produced by T1T_1.

  5. 5
    Step 5

    The final value reflects only T2T_2's calculation, so T1T_1's update is lost.

Lost Update Warning

A transaction can be individually valid yet still lose another transaction’s work. Do not assume that a successful write means the application update was safely merged.

2. Dirty Read

A dirty read occurs when T2T_2 reads a value changed by T1T_1, but T1T_1 later rolls back. Consequently, T2T_2 has used data that never became part of the committed database state.

Example

Assume an account balance is initially 500500.

TimeTransaction T1T_1Transaction T2T_2Visible balance
1Updates balance to 00Uncommitted 00
2Reads balance00
3Rolls back500500

Transaction T2T_2 read 00, but the committed balance is still 500500. If T2T_2 uses the value to approve a purchase, generate a report, or make another update, its decision is based on data that never existed as committed state.

Schedule

W1(B=0),R2(B=0),A1W_1(B{=}0), R_2(B{=}0), A_1

The read is dirty because R2(B)R_2(B) occurs before T1T_1 commits.

Why dirty reads are dangerous

Dirty reads can cause:

  • Incorrect financial or inventory decisions.
  • Reports that include temporary values.
  • Cascading errors in dependent transactions.
  • Inconsistent application behavior when a rollback occurs.
  • Violations of business rules based on uncommitted state.

Prevention

Dirty reads are prevented when a transaction reads only committed data. Typical mechanisms include:

  • READ COMMITTED isolation or stronger.
  • Shared locks that remain effective until the writer commits.
  • MVCC snapshots that hide uncommitted versions.
  • Avoiding database configurations equivalent to READ UNCOMMITTED.

Under PostgreSQL, even the named READ UNCOMMITTED level does not expose dirty data; PostgreSQL treats it as READ COMMITTED.

Footnotes

  1. Transaction Isolation Levels (ODBC) - Microsoft Learn - Defines dirty reads as reads of uncommitted data that may later be rolled back.

  2. PostgreSQL 18 Documentation: Transaction Isolation - Documents PostgreSQL isolation behavior, snapshots, and its implementation-specific guarantees.

3. Unrepeatable Read

An unrepeatable read occurs when a transaction reads a row, another transaction updates or deletes it and commits, and the first transaction reads the row again.

Example

Assume an employee’s salary is initially 60,00060{,}000.

TimeTransaction T1T_1Transaction T2T_2Salary
1Reads salary60,00060{,}000
2Updates salary to 65,00065{,}00065,00065{,}000
3Commits65,00065{,}000
4Reads salary again65,00065{,}000

Within one logical transaction, T1T_1 observed two different committed values for the same row.

Schedule

R1(S=60000),W2(S=65000),C2,R1(S=65000)R_1(S{=}60000), W_2(S{=}65000), C_2, R_1(S{=}65000)

Unlike a dirty read, the second value is committed. The problem is that the database state changed between two reads performed by the same transaction.

Consequences

Unrepeatable reads can cause:

  • A report whose calculations use two different versions of the same row.
  • Incorrect comparisons between an initial and final value.
  • Decisions based on a value that changes during validation.
  • Confusing user experiences in multi-step workflows.

Prevention

Unrepeatable reads can be prevented by:

  • REPEATABLE READ isolation or stronger.
  • Holding read locks until transaction completion.
  • Reading from a consistent MVCC snapshot.
  • Explicitly locking rows with a database-specific FOR UPDATE or FOR SHARE clause when appropriate.

At READ COMMITTED, each statement may see a new committed snapshot, so repeated statements can observe different values. PostgreSQL documents this behavior explicitly.

Difference from a phantom read

An unrepeatable read concerns an existing row whose value changes or whose row disappears. A phantom read concerns the set of rows matching a predicate.

Example:

  • Unrepeatable read: SELECT salary FROM employees WHERE employee_id = 7 returns 60,000andthen60,000 and then 65,000.
  • Phantom read: SELECT * FROM employees WHERE department = 'Sales' returns 10 rows and then 11 because another transaction inserted a matching employee.

Footnotes

  1. Transaction Isolation Levels (ODBC) - Microsoft Learn - Defines nonrepeatable reads as repeated reads of a row returning different data after another transaction commits an update or delete.

  2. PostgreSQL 18 Documentation: Transaction Isolation - Documents PostgreSQL isolation behavior, snapshots, and its implementation-specific guarantees.

Isolation Levels and Common Read Phenomena

The SQL-92 model describes which read anomalies may occur at each isolation level.

Isolation-level comparison

Isolation levelDirty readUnrepeatable readPhantom readTypical trade-off
READ UNCOMMITTEDPossiblePossiblePossibleHighest concurrency, weakest read consistency
READ COMMITTEDPreventedPossiblePossibleCommon balance between consistency and throughput
REPEATABLE READPreventedPreventedMay be possible under the SQL modelStable row reads, potentially more contention
SERIALIZABLEPreventedPreventedPreventedStrongest correctness, possible blocking or retries

This table reflects the conventional SQL isolation model. Specific DBMS implementations can provide stronger behavior or use different mechanisms. For example, PostgreSQL reports that dirty reads are not possible, and its REPEATABLE READ implementation also prevents phantom reads even though the SQL standard permits them at that level.

Isolation levels do not automatically solve every business rule. A transaction can avoid dirty and unrepeatable reads while still experiencing a serialization anomaly, write skew, or a lost-update pattern caused by application logic. The required guarantee must therefore be matched to the operation.

Footnotes

  1. PostgreSQL 18 Documentation: Transaction Isolation - Documents PostgreSQL isolation behavior, snapshots, and its implementation-specific guarantees.

Selecting a Concurrency-Control Strategy

  1. 1
    Step 1

    Determine whether the transaction only reads, performs a read-modify-write, enforces a constraint, or scans a range of rows.

  2. 2
    Step 2

    For committed-only reads, use at least READ COMMITTED. For stable repeated row reads, consider REPEATABLE READ. For cross-row business invariants, consider SERIALIZABLE.

  3. 3
    Step 3

    Pessimistic control acquires locks before conflicts occur. Optimistic control allows work to proceed and validates versions or conflicts before commit.

  4. 4
    Step 4

    Short transactions reduce lock duration, waiting, deadlocks, and the probability of conflicts.

  5. 5
    Step 5

    A deadlock victim, serialization failure, or optimistic version conflict should be rolled back and safely retried when the operation is retryable.

  6. 6
    Step 6

    Use concurrent test sessions to reproduce schedules involving reads, writes, commits, and rollbacks.

Concurrency-Control Techniques

Lock-based control

A lock controls who may read or write an item.

  • A shared lock generally permits multiple readers.
  • An exclusive lock generally permits one writer and prevents conflicting access.
  • A transaction may wait when another transaction holds an incompatible lock.

Two-phase locking has a growing phase, during which locks are acquired, and a shrinking phase, during which locks are released. Strict variants retain write locks until commit or rollback, helping prevent cascading effects and dirty reads.

MVCC

MVCC allows readers to access an appropriate committed version while a writer creates a newer version. This can reduce reader-writer blocking, but the exact guarantees depend on the DBMS and isolation level.

Optimistic concurrency control

Optimistic concurrency control is useful when conflicts are relatively rare.

A version-column pattern is:

1SELECT balance, version 2FROM accounts 3WHERE account_id = 1; 4 5UPDATE accounts 6SET balance = 120, 7 version = version + 1 8WHERE account_id = 1 9 AND version = 4;

If the update affects zero rows, another transaction changed the record first. The application must reload the row, reconcile the change, or retry.

Atomic database operations

Whenever possible, express a modification as one database operation:

1UPDATE inventory 2SET quantity = quantity - 1 3WHERE product_id = 42 4 AND quantity > 0;

The application should verify the affected-row count. An affected-row count of zero may indicate insufficient inventory or a concurrent conflict.

Footnotes

  1. CMU 15-445/645 Lecture Notes: Two-Phase Locking - Explains two-phase locking, lock compatibility, serializability, and deadlock detection.

  2. Chapter 18: Concurrency Control - Database System Concepts - Covers multiversion schemes and the use of old data versions to increase concurrency.

Practical Design Rule

Prefer atomic SQL updates for counters, balances, and stock quantities. If a value must be read before it is written, use a row lock or a version check.

Frequently Asked Questions

Concurrency Control Flashcards

1 / 8
Question · Term

Lost update

Click to reveal
Answer · Definition

Two transactions update from the same old value, and one later write overwrites the other.

Worked Comparison

Consider the following three schedules involving an item XX.

Schedule A: Lost update

R1(X=10),R2(X=10),W1(X=15),W2(X=20),C1,C2R_1(X{=}10), R_2(X{=}10), W_1(X{=}15), W_2(X{=}20), C_1, C_2

Both transactions read 1010. The final value is 2020, so the update producing 1515 is lost.

Schedule B: Dirty read

W1(X=50),R2(X=50),A1W_1(X{=}50), R_2(X{=}50), A_1

Transaction T2T_2 reads 5050 before T1T_1 commits. Since T1T_1 aborts, 5050 was never committed.

Schedule C: Unrepeatable read

R1(X=10),W2(X=20),C2,R1(X=20)R_1(X{=}10), W_2(X{=}20), C_2, R_1(X{=}20)

Transaction T1T_1 reads XX twice and obtains different values because T2T_2 committed an update between the reads.

Diagnostic questions

When examining a schedule, ask:

  1. Did one transaction overwrite another transaction’s calculated result?
  2. Did a read occur before the writer committed?
  3. Did the same transaction read the same row twice after another transaction committed a modification?
  4. Is the problem about a row’s value, or about the membership of a query result set?
  5. Which isolation level or conflict-detection mechanism supplies the required guarantee?

Isolation Is Not a Substitute for Transaction Design

Choosing an isolation level is necessary but not sufficient. Correct transaction boundaries, atomic statements, appropriate locks, constraint checks, and retry handling must work together.

Knowledge Check

Question 1 of 5
Q1Single choice

Which situation describes a lost update?

Explore Related Topics

1

Thrashing in Operating Systems: Causes, Mechanisms, and Control

Thrashing is a severe performance collapse where the operating system spends most of its time handling page faults and swapping pages because the combined working‑set demand of active processes exceeds available physical memory.

  • When D=iWSSiD = \sum_i WSS_i and D>mD > m (total demand > frames), paging dominates execution.
  • The root cause is memory overcommitment—excessive degree of multiprogramming or processes with too large footprints.
  • Symptoms include very high page‑fault rates, intense disk paging activity, and sharply reduced CPU productivity.
  • Classic controls are the working‑set model, page‑fault‑frequency monitoring, lowering multiprogramming, using local replacement, and adding RAM.
  • Preventive rule: only keep a set of active processes whose working sets can collectively fit in RAM.
2

Data Warehouse Systems vs. Operational Database Systems: A Comprehensive Comparison

3

Deadlock Prevention by Breaking Coffman Conditions

Deadlock can be avoided by breaking any one of the four Coffman conditions through system‑level policies.

  • Mutual exclusion: Make resources sharable where feasible (e.g., spooling), though many devices are inherently exclusive.
  • Hold and wait: Require a process to request all needed resources before it starts, eliminating partial holding.
  • No preemption: Allow the OS to force a process to release its current resources when a new request cannot be satisfied.
  • Circular wait: Impose a total order R1<R2<<RnR_1 < R_2 < \dots < R_n on resource types and permit requests only in increasing order, preventing cycles such as P1P2P3P1P_1 \to P_2 \to P_3 \to P_1.