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

Які рівні ізоляції транзакцій існують?

Рівні ізоляції визначають, наскільки строго транзакції захищають одна одну від interference. Існує 4 рівні, від найслабшого до найстрогішого:

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

🟢 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

  1. Read-heavy reporting (>1000 TPS на читання) — надмірний overhead
  2. Довгі batch-операції (>1 хв) — високий ризик serialization failures
  3. Висока конкуренція запису (>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]]