Ответ
В PostgreSQL фантомное чтение (phantom read) предотвращается только на уровне изоляции SERIALIZABLE.
Объяснение уровней изоляции в контексте фантомов:
| Уровень изоляции | Фантомное чтение | Причина в PostgreSQL |
|---|---|---|
READ UNCOMMITTED |
Возможно | Не реализован, ведет себя как READ COMMITTED. |
READ COMMITTED |
Возможно | Видит новые коммиты других транзакций. |
REPEATABLE READ |
Возможно | Ключевой момент: В PostgreSQL REPEATABLE READ блокирует только существующие строки, удовлетворяющие условию WHERE, но не предотвращает вставку новых строк (фантомов) другими транзакциями. |
SERIALIZABLE |
Предотвращено | Использует механизм Serializable Snapshot Isolation (SSI) для обнаружения и отката транзакций, выполнение которых могло бы привести к аномалии сериализуемости, включая фантомы. |
Почему в PostgreSQL REPEATABLE READ допускает фантомы?
В отличие от некоторых других СУБД, PostgreSQL реализует REPEATABLE READ через Snapshot Isolation. Транзакция работает со снимком данных на момент своего начала. Она не видит новые строки, вставленные и закоммиченные после этого снимка. Однако, если такая вставка конфликтует с будущими операциями текущей транзакции (например, попытка обновить несуществующую для неё строку), конфликт не обнаруживается на уровне REPEATABLE READ. SERIALIZABLE добавляет дополнительный механизм отслеживания таких зависимостей и откатывает одну из транзакций.
Пример фантомного чтения на REPEATABLE READ:
-- Транзакция 1
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM users WHERE rating > 80; -- (1) Видит 2 пользователя
-- Транзакция 2 (выполняется параллельно)
INSERT INTO users (name, rating) VALUES ('Alice', 85);
COMMIT;
-- Транзакция 1 (продолжение)
SELECT * FROM users WHERE rating > 80; -- (2) ВСЁ ЕЩЁ видит 2 пользователя (фантома 'Alice' нет)
UPDATE users SET bonus = 100 WHERE rating > 80; -- (3) ОБНОВИТ 2 строки, но также затронет и 'Alice'!
COMMIT; -- После коммита в таблице окажутся 3 пользователя с bonus=100, включая 'Alice'.
Транзакция 1 не увидела фантома при чтении (2), но повлияла на него при обновлении (3). На уровне SERIALIZABLE такая ситуация была бы обнаружена, и одна из транзакций была бы прервана с ошибкой 40001 Serializable isolation violation.