Что такое фантомное чтение (Phantom Read) при работе с транзакциями?

«Что такое фантомное чтение (Phantom Read) при работе с транзакциями?» — вопрос из категории Базы данных, который задают на 22% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Фантомное чтение — это аномалия параллельного доступа, при которой одна транзакция при повторном выполнении одного и того же запроса видит новые строки («фантомы»), которые были добавлены другой, уже завершённой транзакцией в промежутке между этими чтениями.

Отличие от других аномалий:

  • Неповторяющееся чтение (Non-repeatable read): Изменяются данные в существующих строках.
  • Фантомное чтение: Появляются совершенно новые строки, удовлетворяющие условию запроса.

Пример сценария:

-- Уровень изоляции: READ COMMITTED (или ниже)
-- Транзакция A                          | -- Транзакция B
BEGIN TRANSACTION;                       |
SELECT * FROM users WHERE age > 25;      |
-- Результат: 3 строки                   |
                                         | BEGIN TRANSACTION;
                                         | INSERT INTO users (name, age) VALUES ('Carl', 30);
                                         | COMMIT; -- Добавлена новая строка с age=30
SELECT * FROM users WHERE age > 25;      |
-- Результат: 4 строки! Появился "фантом" |
COMMIT;                                  |

Как предотвратить:

  1. Уровень изоляции SERIALIZABLE – Гарантированно предотвращает фантомные чтения, блокируя диапазоны строк.
  2. Использование диапазонных блокировок – Например, SELECT ... FOR UPDATE с условием, которое блокирует вставку в защищаемый диапазон (зависит от СУБД).

В Spring (Java):

@Transactional(isolation = Isolation.SERIALIZABLE)
public void someCriticalMethod() {
    // ... логика, чувствительная к фантомному чтению
}

Почему это проблема: Фантомные чтения могут нарушить бизнес-логику, основанную на агрегации или проверке существования данных (например, подсчёт суммы по условию или проверка уникальности).