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

Что такое Non-repeatable read?

2.0 Middle🔥 201 комментариев
#Работа с данными

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

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

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

Что такое Non-Repeatable Read?

Non-repeatable read (неповторяющееся чтение) — это одна из ключевых проблем параллельного доступа к данным в многопользовательских системах, возникающая при определенных уровнях изоляции транзакций в реляционных базах данных. Это классическая аномалия, когда одна и та же операция чтения, выполненная дважды в рамках одной транзакции, возвращает разные результаты из-за того, что другая, параллельная транзакция успела изменить и зафиксировать эти данные между двумя чтениями.

Механизм возникновения

Рассмотрим классический сценарий на SQL-подобном примере. Представим две параллельные транзакции, T1 и T2, работающие с таблицей users.

-- Транзакция T1 (длительная, например, отчет)
BEGIN TRANSACTION;
-- Первое чтение: пользователь с id=1 имеет баланс 100
SELECT balance FROM users WHERE id = 1; -- Возвращает 100

-- В этот момент выполняется Транзакция T2
BEGIN TRANSACTION;
UPDATE users SET balance = 50 WHERE id = 1;
COMMIT; -- Изменения фиксируются в БД

-- Транзакция T1 продолжает работу
-- Второе чтение ТОГО ЖЕ данными
SELECT balance FROM users WHERE id = 1; -- Возвращает уже 50!
COMMIT;

В результате транзакция T1 получила два разных значения для одного и того же поля (100 и 50) в рамках своей работы. Это и есть non-repeatable read. Для T1 данные «неповторяемы».

Почему это проблема?

  • Нарушение целостности логики внутри транзакции: Логика приложения в T1 могла рассчитывать на консистентность данных между двумя операциями. Например, если бы между чтениями производился расчет (баланс * 0.1), результаты были бы некорректны.
  • Непредсказуемость: Поведение приложения становится зависимым от случайного порядка и времени выполнения параллельных транзакций, что сложно отлаживать.
  • Особенно критично для операций, основанных на условиях (проверка лимита, наличие средств), где решение принимается на основе данных, которые могут измениться к моменту завершения транзакции.

Уровни изоляции и борьба с Non-Repeatable Read

Уровни изоляции транзакций — это механизм СУБД для управления видимостью изменений между транзакциями. Проблема non-repeatable read проявляется на недостаточно строгих уровнях.

  • Read Uncommitted: Самая слабая изоляция. Допускает все аномалии: dirty reads, non-repeatable reads, phantom reads.
  • Read Committed (Уровень по умолчанию во многих СУБД, например, PostgreSQL): Гарантирует, что транзакция видит только зафиксированные данные других транзакций. Однако non-repeatable read здесь все еще возможен, как в примере выше.
  • Repeatable Read (Повторяемое чтение): Ключевой уровень, который предотвращает non-repeatable read. СУБД гарантирует, что любые данные, прочитанные один раз в транзакции, при повторном чтении останутся неизменными. Это достигается с помощью механизмов блокировок (pessimistic concurrency control) или множественных версий данных — MVCC (optimistic concurrency control).
    *   В **MySQL/InnoDB** уровень `REPEATABLE READ` с помощью MVCC создает **снимок данных (snapshot)** на момент первого оператора `SELECT` в транзакции.
    *   В **PostgreSQL** уровень `REPEATABLE READ` также использует snapshot, создаваемый при первом запросе на чтение или изменение.
  • Serializable: Максимальная изоляция, эмулирующая последовательное выполнение транзакций. Предотвращает все аномалии, включая phantom reads.

Non-Repeatable Read vs. Phantom Read

Важно не путать эти две аномалии:

  • Non-Repeatable Read: Касается изменения существующих строк (UPDATE, DELETE).
  • Phantom Read: Касается появления новых строк (INSERT), удовлетворяющих условию предыдущего запроса.

Практическое значение для разработчика Android

Хотя прямой работы с уровнями изоляции в коде Android может и не быть (это ответственность бэкенда), понимание концепции критически важно:

  1. Проектирование API: При разработке сетевых запросов, которые выполняют несколько последовательных чтений для принятия решения, нужно понимать риски неконсистентности данных на стороне сервера.
  2. Локальные базы данных: В SQLite (через Room или напрямую) также существуют транзакции. По умолчанию используется SERIALIZABLE, что предотвращает non-repeatable read. Однако при использовании WAL (Write-Ahead Logging) и правильной настройке транзакций можно столкнуться с особенностями.
  3. Кэширование и синхронизация: Паттерн non-repeatable read может проявиться в вашем собственном коде, если, например, вы читаете данные из кэша в памяти, а параллельный поток (или корутина) эти данные изменяет. Здесь механизмы синхронизации (synchronized, Mutex) или потокобезопасные коллекции играют роль, аналогичную уровням изоляции в БД.

Итог: Non-repeatable read — это фундаментальная проблема конкурентного доступа, которая решается выбором адекватного уровня изоляции транзакций (REPEATABLE READ и выше) на стороне СУБД и грамотным проектированием потоков выполнения в клиентском приложении.