← Назад к вопросам

Как работает изоляция Serializable?

2.2 Middle🔥 91 комментариев
#JVM и память#Работа с данными

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Принцип изоляции Serializable

Изоляция Serializable — это самый строгий уровень изоляции транзакций в системах управления базами данных (СУБД), который обеспечивает полную последовательную (сериализуемую) выполнимость параллельных транзакций. Его ключевая цель — гарантировать, что результат одновременного выполнения нескольких транзакций будет идентичен результату их последовательного (serial) выполнения в каком-либо порядке. Это позволяет избежать всех классических проблем параллельного доступа: грязного чтения (dirty read), неповторяющегося чтения (non-repeatable read) и фантомного чтения (phantom read).

Как достигается изоляция

На практике СУБД реализуют этот уровень с помощью различных механизмов, чаще всего блокировок (pessimistic concurrency control) или управления версиями строк (optimistic concurrency control, MVCC) в сочетании с блокировками диапазонов и предикатов.

1. Механизм блокировок (2PL — Two-Phase Locking)

Классический подход использует строгий двухфазный протокол блокировок (Strict Two-Phase Locking) с добавлением блокировок диапазонов (range locks) или предикатных блокировок.

  • Блокировка на запись (exclusive lock) удерживается до конца транзакции (commit/rollback).
  • Блокировка на чтение (shared lock) также удерживается до конца транзакции. Это предотвращает неповторяющееся чтение.
  • Для предотвращения фантомов используются специальные блокировки диапазонов ключей (next-key locks в InnoDB) или блокировки предикатов. Они блокируют не только существующие строки, удовлетворяющие условию WHERE, но и весь интервал, в который могут попасть будущие строки (фантомы).

Пример проблемы фантомного чтения и ее решения: Представьте две транзакции, работающие с таблицей users, где rating > 5.

-- Транзакция A (Serializable)
START TRANSACTION;
SELECT COUNT(*) FROM users WHERE rating > 5; -- Допустим, результат: 10
-- В этот момент Транзакция B НЕ МОЖЕТ вставить новую запись с rating=7
-- из-за установленной блокировки диапазона (gap lock).
-- Транзакция B (также Serializable)
START TRANSACTION;
INSERT INTO users (name, rating) VALUES ('NewUser', 7); -- Эта операция будет ЗАБЛОКИРОВАНА
-- и будет ждать завершения Транзакции A.
COMMIT;

Если бы не было блокировки диапазона, Транзакция B могла бы вставить строку, и повторный SELECT в Транзакции A показал бы 11 строк (фантом). При уровне Serializable вставка будет заблокирована, обеспечивая изоляцию.

2. Механизм управления версиями строк (MVCC) с проверкой сериализуемости

Современные СУБД (например, PostgreSQL, начиная с версии 9.1) часто используют оптимистичный контроль Serial Snapshot Isolation (SSI) на основе MVCC.

  • Каждая транзакция работает со снимком данных (snapshot) на момент своего начала.
  • Система отслеживает зависимости по записи (rw-dependencies) между параллельными транзакциями. Если обнаруживается цикл в графе таких зависимостей, это означает, что их одновременное выполнение не может быть сериализовано.
  • Транзакция, создающая цикл, прерывается (aborted) с ошибкой сериализации (например, 40001 Serialization Failure). Приложению необходимо повторить ее.

Пример в PostgreSQL:

-- Транзакция 1
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT balance FROM accounts WHERE id = 1; -- Читает snapshot
-- Предположим, balance = 100
UPDATE accounts SET balance = 150 WHERE id = 1;
COMMIT; -- Успешно, если не обнаружено конфликта сериализации

-- Транзакция 2 (выполняется параллельно)
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT balance FROM accounts WHERE id = 1; -- Читает ТОТ ЖЕ snapshot, balance = 100
UPDATE accounts SET balance = 200 WHERE id = 1; -- Эта запись создаст rw-зависимость
COMMIT; -- Если Транзакция 1 уже закоммитилась, здесь будет ОШИБКА сериализации.
-- PostgreSQL откатит эту транзакцию.

Ключевые последствия использования Serializable

  • Максимальная корректность данных: Гарантирует целостность даже в самых сложных сценариях.
  • Низкая производительность и масштабируемость: Высокая стоимость блокировок (в модели 2PL) или большое количество отказов (в модели SSI) из-за конфликтов.
  • Риск взаимоблокировок (deadlocks): Вероятность возрастает, особенно при частых операциях записи. СУБД обнаруживает deadlocks и откатывает одну из транзакций.
  • Необходимость обработки ошибок: При использовании SSI-подхода критически важно, чтобы приложение перехватывало ошибки сериализации и автоматически повторяло всю логику транзакции с самого начала.

Когда использовать?

Уровень SERIALIZABLE следует применять осознанно:

  1. Финансовые операции: Переводы, расчеты, где важна абсолютная точность.
  2. Критичные отчеты: Формирование итоговой отчетности при параллельном изменении данных.
  3. Сложные бизнес-правила: Когда целостность зависит от нескольких связанных запросов в рамках одной транзакции.

В большинстве стандартных приложений (веб-сервисы, CRM) часто достаточно уровней READ COMMITTED или REPEATABLE READ, так как они обеспечивают хороший баланс между целостностью и производительностью. Serializable — это инструмент для специфических случаев, где стоимость ошибки данных превышает стоимость снижения пропускной способности системы.

Как работает изоляция Serializable? | PrepBro