Уровень изолированности Repeatable Read
Уровень изолированности Repeatable Read — это один из четырёх стандартных уровней изолированности транзакций в системах управления базами данных (СУБД), определённых стандартом SQL. Он обеспечивает более строгую изоляцию, чем Read Committed, гарантируя, что повторяющиеся чтения одних и тех же данных в рамках одной транзакции вернут одинаковый результат, независимо от изменений, вносимых параллельными транзакциями. Основное свойство Repeatable Read заключается в предотвращении фантомного чтения в большинстве реализаций, хотя стандарт SQL определяет его как защиту только от «грязного» и неповторяющегося чтения.
История и стандартизация
Концепция уровней изолированности транзакций была формализована в стандарте SQL-92 (ISO/IEC 9075:1992) для решения проблем параллельного доступа к данным. До этого различные СУБД предлагали собственные механизмы блокировок и управления одновременностью. Стандарт определил четыре уровня: Read Uncommitted, Read Committed, Repeatable Read и Serializable. Repeatable Read был введён как компромисс между производительностью и строгой изоляцией: он устраняет два из трёх основных аномалий параллелизма (грязное и неповторяющееся чтение), но допускает фантомное чтение по спецификации.
На практике многие СУБД, такие как PostgreSQL и MySQL (с InnoDB), реализуют Repeatable Read с дополнительными механизмами (например, многоверсионное управление параллельным доступом — MVCC), которые предотвращают и фантомное чтение, фактически делая его эквивалентным Serializable в некоторых сценариях.
Аномалии параллелизма
Уровень Repeatable Read защищает от следующих аномалий:
- Грязное чтение (Dirty Read): чтение данных, изменённых другой транзакцией, которая ещё не завершена (не зафиксирована). Repeatable Read исключает это, так как транзакция видит только зафиксированные данные на момент своего начала или последней точки сохранения.
- Неповторяющееся чтение (Non-repeatable Read): ситуация, когда повторное чтение одной и той же строки в рамках транзакции даёт разные результаты из-за изменений, внесённых другой зафиксированной транзакцией. Repeatable Read гарантирует, что повторное чтение вернёт те же данные, что и при первом чтении, даже если другая транзакция изменила и зафиксировала строку.
Однако стандарт допускает фантомное чтение (Phantom Read) — ситуацию, когда набор строк, удовлетворяющих условию запроса, изменяется между двумя выполнениями одного и того же запроса из-за вставки или удаления строк другой зафиксированной транзакцией. Например, транзакция A выполняет запрос «SELECT * FROM users WHERE age > 30» и получает 10 строк. Затем транзакция B вставляет новую строку с возрастом 35 и фиксируется. При повторном выполнении того же запроса транзакция A увидит 11 строк (фантом). В некоторых СУБД (например, PostgreSQL) Repeatable Read предотвращает и фантомное чтение, используя механизмы MVCC, что делает его более строгим, чем требует стандарт.
Механизмы реализации
Блокировки (Lock-based)
В классической реализации на основе блокировок Repeatable Read использует блокировки чтения (shared locks) и блокировки записи (exclusive locks). При чтении строки транзакция устанавливает блокировку чтения, которая удерживается до конца транзакции. Это предотвращает изменение строки другими транзакциями (они блокируются до завершения первой). Однако блокировки не распространяются на новые строки, которые могут быть вставлены другими транзакциями, что допускает фантомное чтение. Такой подход характерен для Microsoft SQL Server и IBM Db2.
Многоверсионное управление параллельным доступом (MVCC)
Большинство современных СУБД (PostgreSQL, MySQL с InnoDB, Oracle) используют MVCC для реализации Repeatable Read. В этой модели каждая транзакция работает со снимком данных (snapshot), зафиксированным на момент её начала. Изменения, внесённые другими транзакциями после этого момента, не видны до тех пор, пока текущая транзакция не завершится. Это автоматически предотвращает неповторяющееся чтение и, в зависимости от реализации, фантомное чтение. Например, в PostgreSQL Repeatable Read гарантирует, что все запросы в транзакции видят один и тот же снимок данных, включая набор строк, что исключает фантомы.
Различия между СУБД
- PostgreSQL: Repeatable Read фактически эквивалентен Serializable в большинстве случаев, так как использует MVCC с полной изоляцией снимков. Фантомное чтение невозможно.
- MySQL (InnoDB): Repeatable Read также предотвращает фантомное чтение благодаря MVCC и блокировкам gap locks (блокировки промежутков), которые предотвращают вставку новых строк в диапазон, затронутый транзакцией.
- Microsoft SQL Server: По умолчанию Repeatable Read допускает фантомное чтение. Для его предотвращения требуется использование подсказок (например, SERIALIZABLE) или изменение уровня изоляции.
- Oracle: Не поддерживает Repeatable Read как отдельный уровень; вместо этого используется Serializable или Read Committed с возможностью ручного управления блокировками.
Применение
Уровень Repeatable Read применяется в сценариях, где требуется консистентность данных при повторных чтениях, но допустима некоторая гибкость в отношении вставок новых строк. Типичные примеры:
- Финансовые отчёты: генерация отчётов, где суммы по счетам не должны меняться между запросами в рамках одной транзакции.
- Инвентаризация: подсчёт товаров на складе, где важно, чтобы повторное чтение количества не давало разных значений.
- Аналитические запросы: выполнение нескольких запросов, которые должны видеть согласованное состояние данных (например, сравнение продаж за два периода).
Преимущества и недостатки
Преимущества
- Гарантирует повторяемость чтения, что критично для многих бизнес-приложений.
- Предотвращает грязное и неповторяющееся чтение, снижая риск ошибок из-за параллельных изменений.
- В реализациях с MVCC обеспечивает высокую производительность при чтении, так как блокировки чтения не требуются.
Недостатки
- Более высокий уровень изоляции требует больше ресурсов (память для хранения версий строк, блокировки), что может снизить производительность в системах с высокой конкурентностью записи.
- Возможны взаимные блокировки (deadlocks) при использовании блокировок, особенно в СУБД с lock-based реализацией.
- В некоторых СУБД (например, SQL Server) остаётся риск фантомного чтения, что может быть неприемлемо для строгих требований консистентности.
Сравнение с другими уровнями изоляции
| Уровень изоляции | Грязное чтение | Неповторяющееся чтение | Фантомное чтение |
|---|---|---|---|
| Read Uncommitted | Возможно | Возможно | Возможно |
| Read Committed | Невозможно | Возможно | Возможно |
| Repeatable Read | Невозможно | Невозможно | Возможно (по стандарту) |
| Serializable | Невозможно | Невозможно | Невозможно |
На практике, как отмечено выше, многие СУБД реализуют Repeatable Read с защитой от фантомов, что делает его близким к Serializable по свойствам, но с меньшими накладными расходами.
Пример использования
Рассмотрим транзакцию в PostgreSQL, работающую на уровне Repeatable Read:
``sql BEGIN ISOLATION LEVEL REPEATABLE READ; SELECT balance FROM accounts WHERE account_id = 1; -- Возвращает 100 -- Параллельная транзакция изменяет balance на 200 и фиксируется SELECT balance FROM accounts WHERE account_id = 1; -- Всё ещё возвращает 100 (неповторяющееся чтение предотвращено) COMMIT; ``
Если бы использовался Read Committed, второй SELECT вернул бы 200 (неповторяющееся чтение). В Repeatable Read этого не происходит.
Критика и ограничения
Основная критика Repeatable Read связана с неоднозначностью реализации в разных СУБД. Стандарт SQL оставляет место для интерпретации, что приводит к различиям в поведении. Например, в MySQL с InnoDB Repeatable Read предотвращает фантомное чтение, но может вызывать больше взаимных блокировок из-за gap locks. В PostgreSQL, напротив, фантомы исключены, но механизм MVCC требует больше памяти для хранения старых версий строк при длительных транзакциях.
Кроме того, Repeatable Read не решает проблему аномалий сериализации, таких как потерянные обновления (lost updates) или нарушение целостности при параллельных изменениях, что требует перехода на Serializable или использования явных блокировок.
Источники
- ISO/IEC 9075:1992 (SQL-92) — стандарт уровней изоляции.
- Документация PostgreSQL: «Transaction Isolation» (раздел о Repeatable Read).
- Документация MySQL: «InnoDB Transaction Isolation Levels».
- Документация Microsoft SQL Server: «Transaction Isolation Levels».
- Gray, J., & Reuter, A. (1993). Transaction Processing: Concepts and Techniques. Morgan Kaufmann. — главы о параллелизме и изоляции.
- Date, C. J. (2003). An Introduction to Database Systems (8th ed.). Addison-Wesley. — разделы о транзакциях и уровнях изоляции.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →