Уровни изоляции транзакций: READ UNCOMMITTED, READ COMMITTED и др.
Что такое уровни изоляции транзакций? 🔄
Когда несколько транзакций выполняются одновременно, база данных должна обеспечить их корректное взаимодействие. Уровни изоляции определяют, какие "побочные эффекты" одной транзакции могут быть видны другой.
Представьте, что транзакции — это люди в офисе: одни читают документы, другие их меняют. Уровень изоляции — это правила, определяющие, когда изменения становятся видны коллегам.
Проблемы параллельного выполнения
Прежде чем разбирать уровни, познакомимся с тремя ключевыми проблемами:
-
"Грязное" чтение (Dirty Read) 📖
Чтение незафиксированных данных из другой транзакции. Если та транзакция откатится, вы прочитаете мусор. -
Неповторяемое чтение (Non-repeatable Read) 🔄
Повторное чтение тех же данных в рамках одной транзакции возвращает разные результаты, потому что другая транзакция изменила их. -
Фантомное чтение (Phantom Read) 👻
Появление новых строк при повторном выполнении того же запроса, потому что другая транзакция добавила подходящие данные.
Четыре уровня изоляции ANSI SQL
Стандарт SQL определяет четыре уровня (от самого слабого к самому строгому):
1. READ UNCOMMITTED — чтение незафиксированных данных
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
- Разрешает все три проблемы (грязное, неповторяемое и фантомное чтение)
- Самый быстрый, но самый опасный уровень
- Используется редко, только для агрегаций, где точность не критична
Пример:
Транзакция 1 изменяет баланс, но не фиксирует. Транзакция 2 уже видит это изменение. Если Транзакция 1 откатится, Транзакция 2 работала с несуществующими данными.
2. READ COMMITTED — чтение подтверждённых данных
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
- Запрещает грязное чтение ✅
- Разрешает неповторяемое и фантомное чтение ❌
- Дефолтный уровень в PostgreSQL и Oracle
Как работает:
База данных запоминает состояние данных на момент начала каждого оператора SELECT.
Пример:
-- Транзакция 1
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- Транзакция 2 (READ COMMITTED)
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- Не увидит изменения
COMMIT;
-- После COMMIT в Транзакции 1
SELECT balance FROM accounts WHERE id = 1; -- Увидит новое значение
3. REPEATABLE READ — повторяемое чтение
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
- Запрещает грязное и неповторяемое чтение ✅
- Разрешает фантомы ❌
- Дефолтный уровень в MySQL (InnoDB)
Как работает:
База запоминает состояние данных на момент начала транзакции для всех SELECT.
Пример в MySQL:
-- Транзакция 1
BEGIN;
SELECT * FROM accounts WHERE id = 1; -- Читает начальное значение
-- Транзакция 2 изменяет и фиксирует данные
UPDATE accounts SET balance = 500 WHERE id = 1;
COMMIT;
-- Транзакция 1 снова читает
SELECT * FROM accounts WHERE id = 1; -- Увидит старое значение!
4. SERIALIZABLE — полная изоляция
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- Запрещает все три проблемы ✅
- Эмулирует последовательное выполнение транзакций
- Самый медленный, но самый безопасный уровень
Как работает:
База блокирует данные, создавая эффект выполнения транзакций строго по очереди.
Какой уровень выбрать? 🤔
| Уровень | Грязное чтение | Неповторяемое | Фантомы | Производительность |
|---|---|---|---|---|
| READ UNCOMMITTED | Да | Да | Да | ⚡⚡⚡⚡⚡ |
| READ COMMITTED | Нет | Да | Да | ⚡⚡⚡⚡ |
| REPEATABLE READ | Нет | Нет | Да | ⚡⚡⚡ |
| SERIALIZABLE | Нет | Нет | Нет | ⚡ |
Эмпирическое правило:
Используйте самый слабый уровень, который допустим для вашей бизнес-логики. Для финансовых операций — SERIALIZABLE, для аналитических отчётов — READ COMMITTED.
Практический пример: банковский перевод 💸
-- Настройка строгой изоляции для финансовой операции
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- Проверка баланса
SELECT balance FROM accounts WHERE user_id = 1 FOR UPDATE;
-- Если баланс достаточен...
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;
COMMIT;
Обратите внимание на FOR UPDATE — это блокировка строк для предотвращения изменений из других транзакций.
Под капотом: как СУБД реализуют изоляцию
-
Блокировки 🔒
Пессимистичный подход: данные блокируются на время транзакции. -
Версионность (MVCC) 📑
Оптимистичный подход (используется в PostgreSQL): каждое изменение создаёт новую версию строки, старая остаётся для других транзакций. -
Снимки данных (Snapshot Isolation) 📸
Транзакция работает с "фотографией" данных на момент своего начала.