Короткий ответ
По умолчанию PostgreSQL использует Read Committed, при котором каждый отдельный запрос внутри транзакции видит только данные, зафиксированные к моменту его начала; Repeatable Read фиксирует единый снимок данных на момент начала всей транзакции, поэтому повторный такой же запрос внутри неё всегда возвращает одинаковый результат.
Как это работает подробнее
Оба уровня в PostgreSQL реализованы через MVCC без блокировки читающих транзакций записывающими; разница в моменте фиксации «снимка» — на уровне отдельного оператора или на уровне всей транзакции. Repeatable Read защищает от non-repeatable read и phantom read, но не от всех serialization anomalies, в отличие от Serializable.
Пример кода
example.sql
-- Сессия A
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT balance FROM accounts WHERE id = 1; -- видит 100
-- Сессия B (в это время, в другой транзакции)
UPDATE accounts SET balance = 200 WHERE id = 1;
COMMIT;
-- Сессия A, тот же SELECT повторно в той же транзакции
SELECT balance FROM accounts WHERE id = 1; -- всё ещё видит 100, снимок зафиксирован на начало транзакции
COMMIT;Что использовать на практике
- Практика. Read Committed подходит для большинства обычных CRUD-операций; Repeatable Read выбирают для отчётов/аналитики, где важно видеть консистентный снимок на момент начала транзакции.
Подводные камни
- Ошибка. Предполагают, что Read Committed защищает от lost update между чтением и последующим обновлением — на самом деле для этого нужны явные блокировки (SELECT FOR UPDATE) или более строгий уровень изоляции.