Что такое аномалия «фантомное чтение» (Phantom Read)?

Ответ

Фантомное чтение (Phantom Read) — это аномалия конкурентного доступа, при которой одна транзакция в ходе своего выполнения несколько раз выполняет один и тот же запрос с одним и тем же условием выборки (WHERE) и получает разное количество строк.

Это происходит, когда другая параллельная транзакция успевает добавить или удалить строки, которые подпадают под условие выборки первой транзакции, и зафиксировать свои изменения (COMMIT). Новые, "появившиеся из ниоткуда" строки и называются "фантомами".

Отличие от неповторяющегося чтения (Non-Repeatable Read)

Важно не путать эти две аномалии:

  • Неповторяющееся чтение: Изменяются данные в уже существующих строках. (Например, SELECT ... WHERE id = 5 сначала возвращает status = 'pending', а потом status = 'completed').
  • Фантомное чтение: Появляются новые строки или исчезают существующие. (Например, SELECT COUNT(*) WHERE status = 'pending' сначала возвращает 10, а потом 11).

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

  1. Транзакция 1 подсчитывает количество активных задач в проекте 'X': SELECT COUNT(*) FROM tasks WHERE project_id = 'X' AND status = 'active'; — получает результат 5.

  2. Транзакция 2 добавляет новую активную задачу в тот же проект и фиксирует изменения: INSERT INTO tasks (project_id, status) VALUES ('X', 'active'); COMMIT;

  3. Транзакция 1 в рамках своей логики выполняет тот же самый запрос еще раз: SELECT COUNT(*) FROM tasks WHERE project_id = 'X' AND status = 'active'; — и внезапно получает результат 6. Эта новая шестая строка и есть "фантом".

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

  • Уровень изоляции Repeatable Read: В некоторых СУБД (например, PostgreSQL) этот уровень уже предотвращает фантомное чтение. Однако в других (например, MySQL InnoDB) он защищает только от неповторяющегося чтения, но не от фантомов.
  • Уровень изоляции Serializable: Это самый надежный способ полностью исключить фантомные чтения, так как он гарантирует, что транзакции будут выполняться так, как если бы они шли одна за другой.

Ответ 18+ 🔞

Да ты посмотри, какая интересная хуйня! Фантомное чтение, блядь! Это когда ты в базе как будто призраков видишь, ёпта.

Представь себе: сидит транзакция, такая важная, делает свой запрос. Ну, типа, «дай-ка мне посчитать, сколько у меня тут незакрытых дел». Считает: пять штук. Ну, думает, окей, пять так пять.

А тут, сука, параллельно, как чёрт из табакерки, выскакивает другая транзакция — хитрая жопа! — и бац: добавляет шестое дело. Или удаляет одно из этих пяти. И главное — успевает зафиксироваться, гад! «Всё, я сделал, пока!»

И наша первая транзакция, ни о чём не подозревая, опять свой запрос запускает. А там — оп-па! — уже не пять, а шесть! Или четыре! Откуда взялось? Куда делось? Фантом, блядь! Появился из ниоткуда или в никуда испарился. Вот это и есть фантомное чтение, ёперный театр.

Не путай с другой ерундой! Вот есть ещё «неповторяющееся чтение». Это когда ты одну и ту же строку читаешь, а там данные поменялись. Был статус «в работе», а стал «готово». Это как будто тебе подменили бутылку в холодильнике. А фантом — это когда в холодильнике вообще новая бутылка появляется, которой там секунду назад не было. Или одна исчезает. Вот в чём разница, блядь!

Пример, чтобы вообще всё стало ясно, как божий день:

  1. Транзакция 1 (наша, основная):

    SELECT COUNT(*) FROM tasks WHERE project_id = 'X' AND status = 'active';

    Получает ответ: 5. Всё чётко.

  2. Транзакция 2 (этот, пидарас шерстяной, параллельный):

    INSERT INTO tasks (project_id, status) VALUES ('X', 'active');
    COMMIT;

    Тихонечко, как мышь, проскочил и добавил шестую активную задачу. И смылся.

  3. Транзакция 1 (опять, по своей логике):

    SELECT COUNT(*) FROM tasks WHERE project_id = 'X' AND status = 'active';

    Смотрит — а там уже 6! Волнение ебать! Откуда шестая? Это ж фантом, сука! Призрак!

Как от этого спастись, чтобы не было мучительно больно?

  • Уровень изоляции Repeatable Read: Вот тут, блядь, засада. В одних базах (как PostgreSQL) он тебя от фантомов прикроет, молодец. А в других (как MySQL с InnoDB) — нихуя! Он только от «неповторяющегося чтения» защитит, а фантомы так и будут шастать. Подозрение ебать чувствую к таким полумерам.
  • Уровень изоляции Serializable: А вот это, блядь, тяжёлая артиллерия. Всё, пиздец. Транзакции выстраиваются в одну очередь, как в совке за колбасой, и выполняются строго по очереди. Никаких фантомов, никаких гонок. Правда, и скорость может накрыться медным тазом, но зато надёжно, ёбта.

Вот такие дела. База данных — она живая, блядь. То призраков напускает, то строки подменяет. Надо знать, как с этим бороться.