Питання 7 · Розділ 11

Що таке брудне читання (Dirty Read)?

Транзакція Б прочитала значення 500, якого більше не існує. Це "брудні" дані.

Мовні версії: English Russian Ukrainian

🟢 Junior Level

Брудне читання (Dirty Read) — це аномалія, при якій транзакція читає дані, змінені іншою транзакцією, яка ще не завершилася (не зроблен COMMIT).

Простий приклад

Транзакція А: UPDATE accounts SET balance = 500 WHERE id = 1;
Транзакція Б: SELECT balance FROM accounts WHERE id = 1; -- Бачить 500
Транзакція А: ROLLBACK; -- Скасувала зміни, баланс знову 100

Транзакція Б прочитала значення 500, якого більше не існує. Це “брудні” дані.

Чому це погано

Ви приймаєте рішення на основі даних, яких реально немає в базі.

На якому рівні можливе

Тільки на рівні Read Uncommitted. Усі інші рівні забороняють брудне читання.

Де зустрічається

У PostgreSQL та Oracle цього рівня немає — вони автоматично захищають від dirty reads.


🟡 Middle Level

Сценарій виникнення докладно

Час Транзакція А Транзакція Б (Read Uncommitted)
T1 BEGIN; BEGIN;
T2 UPDATE accounts SET balance = 500 WHERE id = 1;  
T3   SELECT balance FROM accounts WHERE id = 1;500
T4 ROLLBACK;  
T5   Працює з балансом 500, реально 100

Наслідки для системи

  • Фінанси: Кредит на основі непідтвердженого балансу
  • Склади: Бронювання неіснуючого товару
  • Інваріанти: Порушення цілісності — бачимо проміжний некоректний стан

Як СУБД захищають від Dirty Read

Терміни:

  • S-lock (Shared) — блокування читання: кілька транзакцій можуть читати одночасно, але ніхто не може писати.
  • X-lock (Exclusive) — блокування запису: лише одна транзакція пише, решта чекають.
  • MVCC — зберігання кількох версій рядка, кожна прив’язана до ID транзакції-автора.

Shared Locks (S-locks)

Транзакція читання чекає зняття exclusive lock (X-lock) із запису. Читання блокується до завершення запису.

MVCC (Multi-Version Concurrency Control)

Читання відбувається з останньої закоміченої версії рядка. Незакомічені зміни створюють тимчасову версію, яку інші не бачать.

Де дозволено

Єдиний рівень — Read Uncommitted.

Важливо: У PostgreSQL цей рівень відсутній (автоматично замінюється на Read Committed). В Oracle — теж.

Dirty Read vs інші аномалії

Dirty Read: ви бачите ЗНАЧЕННЯ, якого ще немає в базі (транзакція-автор зробила ROLLBACK). Non-repeatable Read: ви бачите РЕАЛЬНЕ закомічене значення, але воно змінилося між двома вашими SELECT.

  • Dirty Read: Читання незакомічених даних
  • Non-repeatable Read: Читання закомічених даних, які змінилися між двома SELECT
  • Phantom Read: Поява нових рядків між двома SELECT

🔴 Senior Level

MVCC Protection Mechanism — Deep Dive

PostgreSQL Implementation

In PostgreSQL, every heap tuple contains:

  • t_xmin: transaction ID that inserted this row version
  • t_xmax: transaction ID that deleted/updated this row version

Visibility check for dirty read prevention:

bool TupleIsVisibleToSnapshot(HeapTuple tuple, Snapshot snap) {
    // If inserting transaction is still in progress → NOT visible
    if (TransactionIdIsInProgress(tuple->t_xmin))
        return false;

    // If inserting transaction didn't commit → NOT visible
    if (!TransactionIdDidCommit(tuple->t_xmin))
        return false;

    // If deleting transaction committed → NOT visible
    if (TransactionIdIsValid(tuple->t_xmax) &&
        TransactionIdDidCommit(tuple->t_xmax))
        return false;

    return true;
}

This check is free — it’s part of normal MVCC visibility, no extra overhead to prevent dirty reads.

In-Multi-Version Storage

  • Uncommitted changes create new tuple versions in-place
  • These versions have t_xmin = current (in-progress) transaction ID
  • Other transactions’ snapshots exclude in-progress transaction IDs
  • Result: dirty reads impossible by design, zero extra cost

Why Dirty Reads Are Particularly Dangerous

1. Silent Data Corruption

Unlike crashes or explicit errors, dirty reads produce wrong results without any error. The application continues normally with corrupted data.

2. Cascading Effects

Dirty Read → Wrong Decision → Wrong DB Write → More Dirty Data

The corruption propagates through the system, making debugging extremely difficult.

3. Non-Deterministic Behavior

Dirty reads are timing-dependent. They may occur only under specific load patterns, making them nearly impossible to reproduce in testing.

SQL Server NOLOCK Deep Dive

SELECT * FROM accounts WITH (NOLOCK);

What NOLOCK actually does:

  • No Shared (S) locks acquired
  • No blocking on Exclusive (X) locks
  • Can read uncommitted rows from in-flight transactions
  • Can read rows twice (if page splits occur during scan)
  • Can skip rows (if allocation order scan and pages move)

In practice, NOLOCK can return:

  • Rows that were later rolled back
  • Duplicate rows
  • Missing rows
  • Allocation-order inconsistencies

Historical Context

Early database systems (pre-MVCC) used lock-based concurrency:

  • Readers acquired S-locks, writers acquired X-locks
  • S-lock conflicts with X-lock → readers blocked
  • To avoid blocking, some systems offered Read Uncommitted
  • This was a performance hack, not a feature

Modern MVCC systems (PostgreSQL, Oracle) made this trade-off obsolete:

  • Readers never block writers
  • Writers never block readers
  • Dirty read prevention comes for free

When People Think They Need Dirty Reads

Scenario: “I need fast approximate counts, I don’t care about exactness”

Better alternatives:

  1. SELECT reltuples FROM pg_class WHERE relname = 'table' — PostgreSQL estimate
  2. Materialized views refreshed periodically
  3. Count from read replicas with acceptable lag
  4. External analytics systems (ClickHouse, Druid)

Production Reality

Переважна більшість production-систем не повинна використовувати Read Uncommitted. Єдині винятки — approximate dashboards та internal monitoring. The “performance benefit” is negligible compared to:

  • Risk of corrupted business decisions
  • Debug complexity
  • Data integrity violations
  • Modern alternatives (read replicas, caching)

If you’re on PostgreSQL or Oracle, you can’t even use it — the databases prevent it architecturally.


🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • Dirty Read — читання незакомічених даних іншої транзакції, яка може зробити ROLLBACK
  • Можливе лише на рівні Read Uncommitted; усі інші рівні забороняють
  • PostgreSQL та Oracle архітектурно запобігають dirty reads через MVCC
  • SQL Server використовує WITH (NOLOCK) для dirty reads, але це ризиковано (дублікати, пропуски)
  • Dirty read особливо небезпечний: додаток працює з даними, яких «ніколи не існувало»
  • MVCC prevention dirty reads безкоштовно — readers always see committed versions only

Часті уточнюючі питання:

  • Чому MVCC запобігає dirty reads без overhead? — Uncommitted changes створюють tuple versions з in-progress transaction ID, які не видні іншим snapshot-ам
  • Чим dirty read відрізняється від non-repeatable read? — Dirty read = незакомічені дані, non-repeatable = закомічені, але змінилися
  • Що таке EvalPlanQual? — Механізм PostgreSQL для re-evaluation WHERE при конкурентному UPDATE

Червоні прапорці (НЕ говорити):

  • “NOLOCK — нормальний підхід для production” — може читати дублікати та пропущені рядки
  • “Dirty read = продуктивніше” — у MVCC системах різниці немає
  • “Це рідкісна проблема” — cascading corruption може бути катастрофічною

Пов’язані теми:

  • [[3. Що таке Read Uncommitted]]
  • [[8. Що таке неповторюване читання (Non-Repeatable Read)]]
  • [[9. Що таке фантомне читання (Phantom Read)]]
  • [[2. Які рівні ізоляції транзакцій існують]]