Вопрос 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. Какие уровни изоляции транзакций существуют]]