Изоляция транзакций
Изоляция транзакций — фундаментальный механизм PostgreSQL, обеспечивающий согласованность данных при параллельном выполнении операций.
В основе лежит MVCC (Multiversion Concurrency Control) — многоверсионное управление параллелизмом.
Почему это важно?
-
Гарантирует целостность данных при конкурентном доступе
-
Позволяет избегать блокировок в большинстве сценариев
-
Обеспечивает гибкость через уровни изоляции
Уровни изоляции ANSI/SQL
PostgreSQL поддерживает 4 уровня (от строгого к мягкому):
Уровень | Описание | Аномалии |
---|---|---|
Serializable | Полная изоляция (как последовательное выполнение) | Нет |
Repeatable Read | Гарантирует непротиворечивость "снимка" данных | Фантомные чтения |
Read Committed | Видны только зафиксированные данные (уровень по умолчанию) | Неповторяемые чтения, фантомы |
Read Uncommitted | Видны даже незафиксированные изменения (в PostgreSQL работает как Read Committed) | Все |
Реализация MVCC в PostgreSQL
-
Каждая строка хранит xmin (версия создания) и xmax (версия удаления)
-
Snapshot Isolation: транзакция видит только данные, зафиксированные до её начала
-
VACUUM: очищает "мертвые" версии строк
Конфликты и блокировки
-
Lost Update: перезапись изменений (решается через
SELECT FOR UPDATE
) -
Deadlock: взаимная блокировка (PostgreSQL автоматически обнаруживает)
-
Serialization Failure: при уровне
Serializable
Настройка уровня изоляции
Практические примеры
Задание 1: Демонстрация аномалий
Неповторяемое чтение (Read Committed):Если одна транзакция дважды читает одни и те же данные, но между этими чтениями другая транзакция изменила и зафиксировала эти данные, то первая транзакция увидит разные значения.
В первой сессии после второго чтения данные изменились, хотя транзакция ещё не завершилась.
Фантомные чтения (Repeatable Read):
Рассмотрим пример, который демонстрирует ключевую аномалию уровня изоляции Repeatable Read
, которая проявляется, когда в рамках одной транзакции появляются новые строки, добавленные другими транзакциями.
-
Транзакция видит "снимок" (snapshot) данных на момент своего начала.
-
Она не видит изменения существующих строк (защита от неповторяемых чтений).
-
Но если другая транзакция добавит новые строки (удовлетворяющие условию запроса), они внезапно "появятся" — это и есть фантомные чтения.
-
Первый
SELECT
в Сессии 1 зафиксировал состояние таблицы (count = 100
). -
Сессия 2 добавила новую запись и зафиксировала её.
-
Повторный
SELECT
в Сессии 1 не увидел новую запись, потому чтоRepeatable Read
сохраняет исходный снимок данных.
Примеры опасных сценариев:
-
Финансовые отчёты:
-
Транзакция рассчитывает итоговую сумму по всем платежам.
-
Параллельно добавляется новый платёж → расчёт не учитывает его, но деньги уже списаны.
-
-
Резервирование мест:
-
Система проверяет количество свободных мест.
-
Параллельно кто-то бронирует место → возникает двойное бронирование.
-
Как избежать фантомных чтений?
Решение 1: ИспользоватьSerializable
PostgreSQL использует Serializable Snapshot Isolation (SSI) для блокировки потенциальных конфликтов.
Решение 2: Явные блокировкиСравнение уровней изоляции
Уровень | Фантомные чтения | Неповторяемые чтения | Грязные чтения |
---|---|---|---|
Read Committed | ❌ (возможны) | ❌ | ✅ (запрещено) |
Repeatable Read | ❌ (в PostgreSQL их нет!) | ✅ (запрещены) | ✅ |
Serializable | ✅ (запрещены) | ✅ | ✅ |