Які рівні ізоляції транзакцій існують?
Рівні ізоляції визначають, наскільки строго транзакції захищають одна одну від interference. Існує 4 рівні, від найслабшого до найстрогішого:
🟢 Junior Level
Рівні ізоляції визначають, наскільки строго транзакції захищають одна одну від interference. Існує 4 рівні, від найслабшого до найстрогішого:
1. Read Uncommitted
Найслабший рівень. Транзакція бачить навіть незафіксовані зміни інших транзакцій. Допускає всі аномалії.
2. Read Committed
Транзакція бачить лише зафіксовані дані. Один і той самий запит всередині транзакції може повернути різні результати.
3. Repeatable Read
Гарантує, що дані, прочитані одного разу, не зміняться до кінця транзакції.
4. Serializable
Найстрогіший рівень. Транзакції виконуються так, наче паралелізму не існує взагалі.
Таблиця аномалій
| Рівень | Брудне читання | Неповторюване читання | Фантомне читання |
|---|---|---|---|
| Read Uncommitted | Можливе | Можливе | Можливе |
| Read Committed | Заборонено | Можливе | Можливе |
| Repeatable Read | Заборонено | Заборонено | Можливе* |
| Serializable | Заборонено | Заборонено | Заборонено |
* У MySQL Repeatable Read також запобігає фантомному читанню.
Знімок (snapshot) = миттєва копія всіх даних бази на конкретний момент часу. Транзакція бачить дані такими, якими вони були в момент її початку (або моменту останнього запиту — залежить від рівня ізоляції).
Коли НЕ використовувати Serializable
- Read-heavy reporting (>1000 TPS на читання) — надмірний overhead
- Довгі batch-операції (>1 хв) — високий ризик serialization failures
- Висока конкуренція запису (>50 concurrent writers на одні дані) — постійні відкоти
🟡 Middle Level
Детальний опис кожного рівня
Read Uncommitted
- Транзакція бачить незакомічені зміни інших транзакцій
- Проблема: Якщо інша транзакція зробить ROLLBACK, ви отримаєте дані, яких “ніколи не існувало”
- Використання: Вкра рідко, для приблизної статистики
- PostgreSQL: Цей рівень відсутній — автоматично замінюється на Read Committed. Це архітектурне рішення, а не налаштування — присутній в усіх версіях PG.
Read Committed
- Найпопулярніший рівень за замовчуванням (PostgreSQL, Oracle, SQL Server), тому що дає достатній захист від брудного читання при мінімальних накладних витратах — немає блокувань читання, немає serialization failures.
- Всередині однієї транзакції два однакових SELECT можуть повернути різні результати
- Реалізується через MVCC — знімок даних створюється перед кожним запитом
Repeatable Read
- Знімок даних створюється один раз — при першому запиті в транзакції
- MySQL (InnoDB): Рівень за замовчуванням. Запобігає фантомам через Next-Key Locking (Gap Locks)
- PostgreSQL: При конфлікті оновлень кидає помилку серіалізації
Serializable
- Найстрогіший і найдорожчий рівень
- PostgreSQL: Використовує SSI (Serializable Snapshot Isolation) — відстежує залежності між транзакціями
- Додаток має бути готовий до retry при помилці серіалізації
Вплив на продуктивність
Чим вищий рівень ізоляції:
- Тим більше блокувань або версій рядків має підтримувати БД
- Тим вища ймовірність конфліктів та відкатів
- На Serializable потрібно реалізовувати retry logic
Коли який рівень використовувати
- Read Committed: 90% випадків, default для більшості додатків
- Repeatable Read: Коли потрібна стабільність даних всередині транзакції (звіти)
- Serializable: Фінансові операції, бронювання — де ціна помилки висока
🔴 Senior Level
Реалізація в різних СУБД — ключові відмінності
PostgreSQL Approach
- За замовчуванням: Read Committed
- Використовує MVCC з snapshot isolation
- На Read Committed: новий знімок перед кожним оператором
- На Repeatable Read: один знімок на всю транзакцію, при конфлікті —
ERROR: could not serialize access due to concurrent update - Serializable через SSI (Serializable Snapshot Isolation): граф залежностей, detection cycles, selective abortion
MySQL (InnoDB) Approach
- За замовчуванням: Repeatable Read (історично для statement-based replication)
- Next-Key Locking = Record Lock + Gap Lock
- Prevents phantoms на Repeatable Read (виходить за рамки ANSI SQL)
- Higher deadlock probability через gap locks
MVCC Internal Mechanics
PostgreSQL:
- Кожний tuple рядка має
xmin(transaction ID that inserted) таxmax(transaction ID that deleted/updated) - Visibility rules: транзакція бачить рядок, якщо xmin був committed до snapshot і xmax ще не committed
- Old versions зберігаються в тих самих сторінках → bloat → VACUUM cleanup
MySQL/InnoDB:
- Old versions зберігаються в undo log у rollback segments
- More efficient space usage, але складніший recovery
Serializable Snapshot Isolation (PostgreSQL)
SSI відстежує:
- RW-dependencies: коли транзакція читає дані, які інша транзакція потім змінює
- Conflict detection: будується граф залежностей, шукаються cycles
- Transaction abort: при виявленні dangerous structure одна транзакція abort-иться з
SQLSTATE 40001
Application MUST implement retry logic:
@Retryable(value = {CannotSerializeTransactionException.class}, maxAttempts = 3)
@Transactional(isolation = Isolation.SERIALIZABLE)
public void criticalOperation() { ... }
Performance Benchmarks & Trade-offs
| Рівень | Throughput | Latency | Conflict Rate | Use Case |
|---|---|---|---|---|
| Read Committed | Highest | Lowest | ~0% | General purpose |
| Repeatable Read | Medium | Medium | Low-Medium | Reports, batch processing |
| Serializable | Lowest | Highest | High | Financial, booking |
Production Considerations
- Connection pool exhaustion: Serializable може вимагати більше retries → більше connections
- Long-running transactions: На RR та Serializable тримають старі версії рядків довше → bloat
- Monitoring: Track serialization failure rate, tune accordingly
- Alternative: Використовувати pessimistic locking (SELECT FOR UPDATE) замість Serializable для specific hot paths
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- 4 рівні ізоляції: Read Uncommitted, Read Committed, Repeatable Read, Serializable
- Read Committed — дефолт у PostgreSQL, Oracle, SQL Server; Repeatable Read — дефолт у MySQL
- Read Committed запобігає лише брудному читанню
- Repeatable Read запобігає dirty read + non-repeatable read
- Serializable запобігає всім аномаліям, але вимагає retry logic
- PostgreSQL MVCC: новий snapshot перед кожним оператором на RC, один snapshot на RR
- MySQL InnoDB використовує Next-Key Locking (Record + Gap Lock) для запобігання фантомам на RR
- Serializable у PostgreSQL використовує SSI з dependency graph та selective abort
Часті уточнюючі питання:
- Чому PostgreSQL не має Read Uncommitted? — Архітектурне рішення: MVCC безкоштовно запобігає dirty reads
- Чим Repeatable Read у MySQL відрізняється від PostgreSQL? — MySQL запобігає фантомам через Gap Locks, PG — через MVCC snapshot
- Коли використовувати Serializable? — Фінансові операції, бронювання, де ціна помилки висока
- Що таке serialization failure? — Помилка 40001 при виявленні RW-dependency cycle в SSI
Червоні прапорці (НЕ говорити):
- “Repeatable Read в усіх СУБД працює однаково” — MySQL та PG фундаментально різняться
- “Serializable = просто блокування” — PostgreSQL використовує SSI, не 2PL
- “Read Committed = жодних аномалій” — можливі non-repeatable та phantom reads
Пов’язані теми:
- [[1. Розшифруйте кожну літеру ACID]]
- [[3. Що таке Read Uncommitted]]
- [[4. Що таке Read Committed]]
- [[5. Що таке Repeatable Read]]
- [[6. Що таке Serializable]]