Що таке брудне читання (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. Які рівні ізоляції транзакцій існують]]