← Назад к вопросам

Что такое REPEATABLE READ?

2.0 Middle🔥 132 комментариев
#Базы данных и SQL

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Уровень изоляции REPEATABLE READ

REPEATABLE READ (повторяемое чтение) — это один из четырёх стандартных уровней изоляции транзакций в реляционных СУБД, определённых в стандарте SQL (ANSI/ISO). Этот уровень обеспечивает гарантию того, что данные, прочитанные в рамках одной транзакции, останутся неизменными до её завершения, даже если другие параллельные транзакции пытаются их изменить. Главная цель — предотвратить неповторяемое чтение (non-repeatable read).

Основные гарантии и особенности

На уровне REPEATABLE READ обеспечиваются следующие гарантии:

  • Предотвращение "грязного" чтения (Dirty Read): Транзакция не может видеть незафиксированные изменения других транзакций.
  • Предотвращение неповторяемого чтения (Non-Repeatable Read): Если транзакция повторно читает одни и те же данные, она гарантированно увидит их в том же состоянии, что и при первом чтении. Другие транзакции не могут изменять (UPDATE) или удалять (DELETE) эти строки до завершения текущей транзакции.
  • Допущение фантомного чтения (Phantom Read): Это ключевое отличие от более строгого уровня SERIALIZABLE. Транзакция может "увидеть" новые строки (INSERT), добавленные другими транзакциями и зафиксированные после начала текущей транзакции.

Механизм реализации

В большинстве СУБД (таких как MySQL с движком InnoDB, PostgreSQL) REPEATABLE READ реализуется с помощью системы управления параллельным доступом на основе многоверсионности (MVCC — Multi-Version Concurrency Control).

Принцип работы MVCC для REPEATABLE READ:

  1. Каждая транзакция в момент своего начала получает уникальный идентификатор или "снимок" (snapshot).
  2. При выполнении операций SELECT транзакция видит только те версии данных, которые были зафиксированы на момент начала этой транзакции.
  3. Операции UPDATE и DELETE внутри транзакции блокируют соответствующие строки (с помощью shared или exclusive locks), предотвращая их изменение другими транзакциями.
  4. Операции INSERT других транзакций не блокируются, что и приводит к возможности фантомного чтения.

Пример в контексте PHP и MySQL

Рассмотрим сценарий с двумя параллельными транзакциями.

-- Транзакция 1 (Session 1) - Уровень изоляции REPEATABLE READ
START TRANSACTION;
-- Снимок данных создан здесь

SELECT balance FROM accounts WHERE user_id = 1;
-- Допустим, вернулось 1000
-- Транзакция 2 (Session 2)
START TRANSACTION;
UPDATE accounts SET balance = balance - 200 WHERE user_id = 1;
COMMIT; -- Изменение фиксируется в БД
-- Возвращаемся в Транзакцию 1
SELECT balance FROM accounts WHERE user_id = 1;
-- ПОВТОРНОЕ ЧТЕНИЕ: ВСЁ РАВНО вернёт 1000, а не 800!
-- Гарантия REPEATABLE READ соблюдена. Изменение от Транзакции 2 не видно.
COMMIT;
-- После COMMIT все последующие запросы увидят актуальное значение (800).

Пример возможного фантомного чтения:

-- Транзакция 1
START TRANSACTION;
SELECT COUNT(*) FROM orders WHERE user_id = 5; -- Вернуло 3
-- Транзакция 2
START TRANSACTION;
INSERT INTO orders (user_id, amount) VALUES (5, 99);
COMMIT;
-- Транзакция 1
SELECT COUNT(*) FROM orders WHERE user_id = 5; -- МОЖЕТ вернуть 3 (в MySQL/PostgreSQL с MVCC), а может и 4 (зависит от реализации).
-- В REPEATABLE READ фантомные строки обычно НЕ появляются в рамках одного запроса в MySQL InnoDB,
-- но стандарт ANSI допускает такую возможность. Это важный нюанс для обсуждения.
COMMIT;

REPEATABLE READ в разных СУБД

  • MySQL (InnoDB): Уровень по умолчанию. Реализация настолько строгая, что с помощью next-key locks (сочетание record locks и gap locks) предотвращает не только неповторяемое, но и фантомное чтение в большинстве сценариев, что фактически приближает его к SERIALIZABLE, но с лучшей производительностью.
  • PostgreSQL: Уровень также реализует полноценный снапшот на начало транзакции и предотвращает фантомное чтение для всех запросов внутри неё.
  • Oracle Database: Уровень READ COMMITTED является уровнем по умолчанию, а для REPEATABLE READ используется конструкция SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, которая работает на основе снапшотов.

Преимущества и недостатки

Преимущества:

  • Консистентность данных внутри бизнес-транзакции, требующих многократного обращения к одним и тем же строкам (например, проверка баланса перед списанием и само списание).
  • Высокая производительность по сравнению с уровнем SERIALIZABLE, так как блокировки менее строгие и допускается больше параллелизма.

Недостатки:

  • Риск блокировок (deadlocks): Из-за удержания блокировок на прочитанных данных до конца транзакции увеличивается вероятность взаимоблокировок. Транзакции должны быть короткими.
  • Более высокое потребление ресурсов: СУБД должна хранить несколько версий строк для обеспечения снапшотов, что увеличивает нагрузку на память и хранилище.
  • Сложность понимания: Для разработчиков, не знакомых с MVCC, поведение может быть неочевидным (например, "почему я не вижу только что закоммиченные данные?").

Практическое применение в PHP-разработке

В веб-приложениях на PHP с использованием ORM (например, Doctrine) или прямых запросов к БД:

  1. Финансовые операции: Переводы между счетами, где нужно гарантировать, что баланс не изменится между проверкой и списанием.
  2. Сложные отчёты: Формирование отчёта, который требует согласованных данных на момент начала генерации, даже если процесс занимает некоторое время.
  3. Агрегация данных: Последовательные вычисления, зависящие от одних и тех же базовых данных.

Установить уровень изоляции в PHP-коде можно так:

<?php
// Пример с PDO для MySQL
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$pdo->exec("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");

$pdo->beginTransaction();
// ... логика транзакции ...
$pdo->commit();

Вывод: REPEATABLE READ — это баланс между строгой консистентностью данных и производительностью. Он является надежным выбором для большинства бизнес-транзакций, где критически важна согласованность повторяющихся чтений, и при этом позволяет системе масштабироваться лучше, чем полностью сериализуемый доступ. Понимание его работы, особенно в контексте конкретной используемой СУБД, — обязательный навык для backend-разработчика, работающего с данными.

Что такое REPEATABLE READ? | PrepBro