Что такое грязное чтение (Dirty Read)?
Транзакция Б прочитала значение 500, которого больше не существует. Это "грязные" данные.
🟢 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 versiont_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:
SELECT reltuples FROM pg_class WHERE relname = 'table'— PostgreSQL estimate- Materialized views refreshed periodically
- Count from read replicas with acceptable lag
- 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. Какие уровни изоляции транзакций существуют]]