Уровни изоляции транзакций в SQL
Уровни изоляции определяют, как изменения, сделанные одной транзакцией, видны другим параллельным транзакциям. Стандарт SQL определяет 4 уровня, каждый из которых решает определённые проблемы конкурентного доступа.
Проблемы параллельного доступа
Прежде чем разбирать уровни, нужно понимать, какие аномалии они предотвращают:
- Dirty Read (грязное чтение) — чтение незафиксированных изменений другой транзакции
- Non-repeatable Read (неповторяющееся чтение) — при повторном чтении в рамках одной транзакции данные меняются из-за коммита другой транзакции
- Phantom Read (фантомное чтение) — при повторном выполнении одного запроса появляются новые строки, добавленные другой транзакцией
Уровни изоляции
1. Read Uncommitted (чтение незафиксированных данных)
Самый низкий уровень. Транзакция видит изменения других транзакций даже до их фиксации.
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
- Решает: ничего
- Допускает: Dirty Read, Non-repeatable Read, Phantom Read
- Использование: крайне редко, только когда точность данных не критична (например, счётчики посещений)
2. Read Committed (чтение зафиксированных данных)
Транзакция видит только зафиксированные изменения. Это уровень по умолчанию в PostgreSQL и многих других СУБД.
- Решает: Dirty Read
- Допускает: Non-repeatable Read, Phantom Read
- Особенность: в PostgreSQL реализован через MVCC — каждая транзакция видит снимок данных на момент начала запроса (statement-level snapshot)
3. Repeatable Read (повторяемое чтение)
Гарантирует, что при повторном чтении в рамках одной транзакции данные не изменятся.
- Решает: Dirty Read, Non-repeatable Read
- Допускает: Phantom Read (в стандарте SQL)
- Особенности реализации:
- В PostgreSQL: предотвращает и Phantom Read (использует snapshot на момент первой операции в транзакции)
- В MySQL/InnoDB: использует блокировки чтения и next-key locks, предотвращая фантомы
- В MSSQL: допускает фантомы, если не использовать блокировки
4. Serializable (сериализуемый)
Самый строгий уровень. Транзакции выполняются так, как если бы они шли последовательно.
- Решает: все проблемы (Dirty Read, Non-repeatable Read, Phantom Read)
- Реализация:
- Pessimistic: блокировки всех читаемых/изменяемых данных
- Optimistic (SSI — Serializable Snapshot Isolation): разрешает конфликты при коммите, откатывая одну из конфликтующих транзакций
Практические аспекты для Go-разработчика
Выбор уровня по умолчанию:
- PostgreSQL: Read Committed — оптимальный баланс производительности и консистентности
- MySQL: Repeatable Read (из-за особенностей репликации)
Типичные сценарии:
// Явное указание уровня изоляции
tx, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelSerializable,
})
Когда повышать изоляцию:
- Финансовые операции — Serializable
- Аналитические отчёты — Repeatable Read для консистентности данных
- Высоконагруженные системы — Read Committed + оптимистичные блокировки
Подводные камни:
- Serializable может значительно снизить производительность из-за конфликтов и откатов
- В PostgreSQL Repeatable Read не защищает от ошибок сериализации (serialization failures) при конкурентной записи
- Read Uncommitted в PostgreSQL работает как Read Committed — СУБД не поддерживает грязное чтение
Рекомендации
- Начинайте с уровня по умолчанию (Read Committed для PostgreSQL)
- Повышайте изоляцию только при явных проблемах с консистентностью
- Для критичных операций используйте явные блокировки (
SELECT FOR UPDATE) вместо повышения уровня изоляции всей транзакции - При использовании Repeatable Read или Serializable всегда обрабатывайте ошибки сериализации (код 40001 в PostgreSQL)