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

Ответ

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

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

  • Неповторяющееся чтение (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() {
    // ... логика, чувствительная к фантомному чтению
}

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

Ответ 18+ 🔞

Да ты посмотри, какая дичь может твориться в базе, если за ней не следить! Есть такая штука — фантомное чтение. Это когда ты в одной транзакции два раза подряд одно и то же спрашиваешь, а тебе во второй раз уже другие данные подсовывают, будто призраки материализовались! Новые строки, которых в первый раз на хуй не было, вдруг появляются, потому что другая транзакция успела их впендюрить и закрыться.

Чем это не похоже на другую ерунду:

  • Неповторяющееся чтение: Это когда данные в старых строках меняются, как погода в Питере.
  • Фантомное чтение: А это когда совершенно новые строки, сука, из ниоткуда возникают, под твоё условие подходят.

Смотри, как это выглядит в жизни:

-- Уровень изоляции: 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:

@Transactional(isolation = Isolation.SERIALIZABLE)
public void someCriticalMethod() {
    // ... тут твоя сверхважная логика, где появление призраков недопустимо
}

А проблема-то в чём, спросишь? Да в том, что вся твоя хитрая логика, которая на подсчётах или проверках «а был ли мальчик?» построена, может накрыться медным тазом. Представь, ты сумму по условию посчитал, решил, что всё ок, а в момент между проверкой и действием уже влез какой-нибудь фантом и всё испохабил. Короче, доверия к таким данным — ноль ебать.