Ответ
Фантомное чтение (Phantom Read) — это аномалия конкурентного доступа, при которой одна транзакция в ходе своего выполнения несколько раз выполняет один и тот же запрос с одним и тем же условием выборки (WHERE) и получает разное количество строк.
Это происходит, когда другая параллельная транзакция успевает добавить или удалить строки, которые подпадают под условие выборки первой транзакции, и зафиксировать свои изменения (COMMIT). Новые, "появившиеся из ниоткуда" строки и называются "фантомами".
Отличие от неповторяющегося чтения (Non-Repeatable Read)
Важно не путать эти две аномалии:
- Неповторяющееся чтение: Изменяются данные в уже существующих строках. (Например,
SELECT ... WHERE id = 5сначала возвращаетstatus = 'pending', а потомstatus = 'completed'). - Фантомное чтение: Появляются новые строки или исчезают существующие. (Например,
SELECT COUNT(*) WHERE status = 'pending'сначала возвращает10, а потом11).
Пример сценария
-
Транзакция 1 подсчитывает количество активных задач в проекте 'X':
SELECT COUNT(*) FROM tasks WHERE project_id = 'X' AND status = 'active';— получает результат5. -
Транзакция 2 добавляет новую активную задачу в тот же проект и фиксирует изменения:
INSERT INTO tasks (project_id, status) VALUES ('X', 'active');COMMIT; -
Транзакция 1 в рамках своей логики выполняет тот же самый запрос еще раз:
SELECT COUNT(*) FROM tasks WHERE project_id = 'X' AND status = 'active';— и внезапно получает результат6. Эта новая шестая строка и есть "фантом".
Как предотвратить
- Уровень изоляции
Repeatable Read: В некоторых СУБД (например, PostgreSQL) этот уровень уже предотвращает фантомное чтение. Однако в других (например, MySQL InnoDB) он защищает только от неповторяющегося чтения, но не от фантомов. - Уровень изоляции
Serializable: Это самый надежный способ полностью исключить фантомные чтения, так как он гарантирует, что транзакции будут выполняться так, как если бы они шли одна за другой.
Ответ 18+ 🔞
Да ты посмотри, какая интересная хуйня! Фантомное чтение, блядь! Это когда ты в базе как будто призраков видишь, ёпта.
Представь себе: сидит транзакция, такая важная, делает свой запрос. Ну, типа, «дай-ка мне посчитать, сколько у меня тут незакрытых дел». Считает: пять штук. Ну, думает, окей, пять так пять.
А тут, сука, параллельно, как чёрт из табакерки, выскакивает другая транзакция — хитрая жопа! — и бац: добавляет шестое дело. Или удаляет одно из этих пяти. И главное — успевает зафиксироваться, гад! «Всё, я сделал, пока!»
И наша первая транзакция, ни о чём не подозревая, опять свой запрос запускает. А там — оп-па! — уже не пять, а шесть! Или четыре! Откуда взялось? Куда делось? Фантом, блядь! Появился из ниоткуда или в никуда испарился. Вот это и есть фантомное чтение, ёперный театр.
Не путай с другой ерундой! Вот есть ещё «неповторяющееся чтение». Это когда ты одну и ту же строку читаешь, а там данные поменялись. Был статус «в работе», а стал «готово». Это как будто тебе подменили бутылку в холодильнике. А фантом — это когда в холодильнике вообще новая бутылка появляется, которой там секунду назад не было. Или одна исчезает. Вот в чём разница, блядь!
Пример, чтобы вообще всё стало ясно, как божий день:
-
Транзакция 1 (наша, основная):
SELECT COUNT(*) FROM tasks WHERE project_id = 'X' AND status = 'active';Получает ответ:
5. Всё чётко. -
Транзакция 2 (этот, пидарас шерстяной, параллельный):
INSERT INTO tasks (project_id, status) VALUES ('X', 'active'); COMMIT;Тихонечко, как мышь, проскочил и добавил шестую активную задачу. И смылся.
-
Транзакция 1 (опять, по своей логике):
SELECT COUNT(*) FROM tasks WHERE project_id = 'X' AND status = 'active';Смотрит — а там уже
6! Волнение ебать! Откуда шестая? Это ж фантом, сука! Призрак!
Как от этого спастись, чтобы не было мучительно больно?
- Уровень изоляции
Repeatable Read: Вот тут, блядь, засада. В одних базах (как PostgreSQL) он тебя от фантомов прикроет, молодец. А в других (как MySQL с InnoDB) — нихуя! Он только от «неповторяющегося чтения» защитит, а фантомы так и будут шастать. Подозрение ебать чувствую к таким полумерам. - Уровень изоляции
Serializable: А вот это, блядь, тяжёлая артиллерия. Всё, пиздец. Транзакции выстраиваются в одну очередь, как в совке за колбасой, и выполняются строго по очереди. Никаких фантомов, никаких гонок. Правда, и скорость может накрыться медным тазом, но зато надёжно, ёбта.
Вот такие дела. База данных — она живая, блядь. То призраков напускает, то строки подменяет. Надо знать, как с этим бороться.