Ответ
Фантомное чтение (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
: Это самый надежный способ полностью исключить фантомные чтения, так как он гарантирует, что транзакции будут выполняться так, как если бы они шли одна за другой.