Repeatable Read: согласованный снимок для нескольких запросов

2 мин чтения

Сравниваем Read Committed и Repeatable Read на отчёте, который выполняет несколько запросов.

Один снимок на несколько чтений
  1. Начало транзакцииВыбираем уровень Repeatable Read
  2. Первое чтениеФиксируется снимок видимых данных
  3. Следующие чтенияИспользуют тот же снимок до конца транзакции

Отчёт сначала посчитал заказы, затем их общую сумму. Между запросами добавился ещё один заказ, и две цифры уже относятся к разным состояниям базы. В PostgreSQL 12 режим Repeatable Read позволяет читать один снимок на протяжении транзакции. Увидим разницу в двух соединениях и обсудим, когда такой снимок нужен.

Готовим данные

CREATE TABLE demo_report (id integer PRIMARY KEY, amount integer NOT NULL);
INSERT INTO demo_report VALUES (1, 100);
-- Соединение A
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT count(*), sum(amount) FROM demo_report;

Первое обычное чтение закрепляет снимок для транзакции Repeatable Read. Теперь оставьте соединение A открытым и выполните добавление из второго соединения. Важно завершить его фиксацией: незавершённые изменения другого участника не позволяют показать нужное различие между уровнями изоляции.

-- Соединение B, режим autocommit
INSERT INTO demo_report VALUES (2, 200);
-- Снова соединение A
SELECT count(*), sum(amount) FROM demo_report;
COMMIT;
SELECT count(*), sum(amount) FROM demo_report;

Что должно измениться

Внутри транзакции A ожидается прежняя пара: одна строка и сумма сто. После COMMIT новый запрос должен увидеть две строки и сумму триста. Для сравнения очистите тестовые данные до исходного состояния и повторите сценарий с Read Committed: там каждое новое выражение получает собственный снимок. Ожидаемые числа описывают контролируемую последовательность без посторонних записей.

Не держите снимок слишком долго

Не оставляйте отчётную транзакцию открытой на время разговора с пользователем или ожидания внешнего API. Длительный снимок удерживает старые версии строк и осложняет обслуживание активно меняющихся таблиц. Начинайте транзакцию непосредственно перед чтением и завершайте после получения необходимых данных. Если отчёт строится несколькими обработчиками, явно проверьте, используют ли они одно соединение и одну транзакцию. Само совпадение уровня изоляции не объединяет независимые снимки.

Одинаковый снимок ещё не защищает любое правило приложения. Два участника могут прочитать одно состояние, изменить разные строки и нарушить общее ограничение. Для таких случаев нужны блокировки либо Serializable с повтором всей транзакции при ошибке сериализации. У отчёта отдельно проверьте снимок, своевременное завершение и обработку отказа.

Источники и дальнейшее чтение