Приведите практический пример возникновения аномалии на уровне изоляции транзакций Repeatable Read.

Ответ

Repeatable Read — это уровень изоляции, гарантирующий, что данные, прочитанные в рамках одной транзакции, не будут изменены другими зафиксированными транзакциями до её завершения. Это предотвращает аномалию неповторяющегося чтения.

Практический пример (аномалия «фантомное чтение»): Две транзакции работают с таблицей orders, где статус processed = false.

-- Транзакция 1 (T1) - Отчет по необработанным заказам
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT COUNT(*) FROM orders WHERE processed = false; -- Результат: 5

-- Транзакция 2 (T2) - Параллельная обработка
BEGIN;
UPDATE orders SET processed = true WHERE id = 1;
COMMIT;

-- T1 повторяет чтение. При Repeatable Read видит те же 5 строк.
-- Фантомное чтение здесь НЕ предотвращается.
SELECT COUNT(*) FROM orders WHERE processed = false; -- Всё ещё 5 (старые данные)

-- Однако, если T2 вставит НОВУЮ строку...
-- (В параллельной сессии)
BEGIN;
INSERT INTO orders (id, processed) VALUES (100, false);
COMMIT;

-- ...то T1 при повторном запросе может её увидеть (фантом).
SELECT COUNT(*) FROM orders WHERE processed = false; -- Может стать 6 (фантомная строка)
COMMIT;

Ключевой момент: REPEATABLE READ гарантирует, что уже прочитанные строки не изменятся, но не блокирует вставку новых строк (фантомов), в отличие от SERIALIZABLE. В некоторых СУБД (например, PostgreSQL) для предотвращения фантомов на этом уровне используются блокировки диапазонов.

Ответ 18+ 🔞

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

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

Вот одна транзакция (T1) начинает отчёт по необработанным заказам делать, под уровнем REPEATABLE READ.

BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT COUNT(*) FROM orders WHERE processed = false; -- Допустим, насчитала она 5 штук, блядь.

А параллельно, сука, другая транзакция (T2) уже работает, обрабатывает заказы.

BEGIN;
UPDATE orders SET processed = true WHERE id = 1; -- Обработала один, гадина.
COMMIT;

И вот наша первая транзакция, ни о чём не подозревая, снова запрос делает.

-- T1 повторяет чтение. При Repeatable Read видит те же 5 строк.
-- Фантомное чтение здесь НЕ предотвращается.
SELECT COUNT(*) FROM orders WHERE processed = false; -- Всё ещё 5 (старые данные)

И она видит, блядь, те же самые 5! Потому что строку, которую она уже видела (с id=1), ей теперь показывают в старом, необновлённом виде. Гарантия работает, ебать её в сраку. Она не видит, что одну уже обработали.

НО! А вот если эта вторая транзакция, хитрая жопа, не просто обновит, а вставит новую строку, вот тут-то и начинается цирк, блядь!

-- (В параллельной сессии, пока T1 ещё не закончилась)
BEGIN;
INSERT INTO orders (id, processed) VALUES (100, false); -- Бац, и новый необработанный заказ!
COMMIT;

И наша T1, довольная такая, снова запрос делает:

SELECT COUNT(*) FROM orders WHERE processed = false; -- Может стать 6 (фантомная строка)
COMMIT;

И тут она, сука, может увидеть уже 6! Вот эта новая, только что вставленная строка — это и есть ёбаный фантом, который просочился. REPEATABLE READ от этой хуйни по умолчанию не защищает, потому что он гарантирует неизменность только тех строк, которые ты уже прочитал, а не всего диапазона, куда могут влезть новые.

Хотя, блядь, в некоторых базах, типа PostgreSQL, они там умные, на этом же уровне могут и диапазоны заблокировать, чтобы фантомов не было. Но это уже, как говорится, особенности реализации, ёпта. В общем, запомни: REPEATABLE READ — это про то, что увидел, то твоё и останется. А что там новенькое подкрадётся — это уже лотерея, блядь.