Ответ
Repeatable Read — это уровень изоляции транзакций, который гарантирует, что любые строки, прочитанные в течение транзакции, не будут изменены другими транзакциями до её завершения. Это предотвращает неповторяющиеся чтения. Однако фантомные чтения (появление новых строк) всё ещё возможны.
Пример на SQL (PostgreSQL):
-- Сессия 1: Устанавливаем уровень изоляции и начинаем транзакцию
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- Первое чтение баланса
SELECT balance FROM accounts WHERE id = 1;
-- Результат: 100
-- В это время Сессия 2 выполняет и коммитит:
-- UPDATE accounts SET balance = 200 WHERE id = 1;
-- Повторное чтение в Сессии 1 покажет старое значение
SELECT balance FROM accounts WHERE id = 1;
-- Результат: 100 (значение не изменилось, неповторяющееся чтение предотвращено)
COMMIT;
-- После коммита последующие запросы увидят новое значение (200).
Почему это важно? Этот уровень обеспечивает консистентность данных для операций, которые несколько раз читают одни и те же строки (например, для проверок и вычислений). Реализация обычно использует блокировки диапазонов ключей или механизм многоверсионности (MVCC) для удержания снапшота данных.
Ответ 18+ 🔞
Ну, слушай, вот есть у тебя эта самая транзакция, как будто она в своём собственном, блядь, мирке сидит. Поставил ты ей уровень REPEATABLE READ — и всё, она как будто снимок данных сделала, заморозила, блядь, момент во времени. И пока она не закончится, она будет видеть одни и те же данные, которые прочитала в первый раз. Даже если снаружи, сука, какой-нибудь другой процесс уже всё обоссал и перезаписал.
То есть, представь: ты в начале транзакции посмотрел баланс на счёте — там 100 рублей. А потом, пока ты там свои вычисления делаешь, другая сессия пришла и нахуярила туда 200. Так вот, в рамках твоей транзакции ты второй раз посмотришь — и опять увидишь 100, старые добрые 100. Никаких неповторяющихся чтений, ёпта! Как будто время остановилось. Красота.
Но, блядь, есть нюанс, как всегда. Фантомные чтения — появление новых строк — он всё ещё может случиться. Ты снапшот-то старых данных держишь, но если кто-то снаружи новые записи в таблицу впендюрит, твоя транзакция их может и не увидеть... или увидеть, смотря как СУБД реализовала. В общем, головняк.
Вот смотри на примере, чтобы совсем пиздецки понятно стало:
-- Сессия 1: Засела в транзакции, как в окопе
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- Глянула баланс первый раз
SELECT balance FROM accounts WHERE id = 1;
-- Видит: 100. Запомнила.
-- Тут, блядь, Сессия 2 врывается как угорелая и делает:
-- UPDATE accounts SET balance = 200 WHERE id = 1;
-- И сразу COMMIT! Баланс уже 200 для всего мира.
-- А наша первая сессия, ебать её в сраку, снова смотрит:
SELECT balance FROM accounts WHERE id = 1;
-- И видит, блядь, опять 100! Потому что она в REPEATABLE READ, она живёт в прошлом. Неповторяющееся чтение предотвращено, ура.
COMMIT;
-- А вот только после коммита она вылезет из своего окопа и увидит новую реальность, где баланс уже 200.
И зачем это всё, спрашивается? А затем, сука, чтобы твоя логика не поехала крышей. Вот представь, ты два раза подряд читаешь одни и те же данные, чтобы что-то проверить или посчитать. А они у тебя между чтениями меняются! Это ж пиздец, волнение ебать, алгоритм сломается. REPEATABLE READ такой: "Успокойся, чувак, я тебе консистентную картинку держу".
Под капотом это либо через блокировки делается (когда ты читаешь, ты как бы "замораживаешь" эти строки для других), либо через многоверсионность (MVCC) — когда для каждой транзакции хранится её личный снимок данных на момент начала. В общем, магия, но полезная.