Открыть сервис

Многоверсионное управление конкурентным доступом

Многоверсионное управление конкурентным доступом (англ. Multiversion Concurrency Control, MVCC) — это метод управления параллельным доступом к данным в системах управления базами данных (СУБД), файловых системах и других системах хранения, при котором каждой транзакции предоставляется «снимок» (snapshot) данных на определённый момент времени. Вместо блокировки строк или таблиц при записи MVCC создаёт новую версию изменяемого объекта, сохраняя при этом старые версии для чтения другими транзакциями. Это позволяет одновременно выполнять читающие и пишущие транзакции без взаимных блокировок, обеспечивая изоляцию транзакций и высокую производительность в многопользовательских средах.

История

Идея многоверсионности восходит к ранним работам по временным базам данных и системам с версионированием. Первые реализации MVCC появились в исследовательских проектах 1970-х годов. Одной из первых коммерческих СУБД, внедрившей MVCC, стала Oracle (версия 7, 1992 год), где этот метод был реализован для обеспечения изоляции чтения без блокировок. Впоследствии MVCC был принят многими другими СУБД: PostgreSQL (с 1996 года), MySQL (с движком InnoDB, 2001 год), Microsoft SQL Server (с версии 2005 года, опция READ_COMMITTED_SNAPSHOT), SQLite (с версии 3.0, 2004 год), а также в NoSQL-системах, таких как MongoDB (с версии 3.0, 2015 год) и CouchDB.

Принцип работы

Основная идея MVCC заключается в том, что каждая операция записи создаёт новую версию записи, а не перезаписывает существующую. Каждая версия помечается временной меткой (timestamp) или идентификатором транзакции, которая её создала. Транзакция, выполняющая чтение, получает доступ к снимку данных, соответствующему определённому моменту времени (обычно — моменту начала транзакции или моменту выполнения оператора). Таким образом, читающая транзакция видит согласованное состояние базы данных, не зависящее от изменений, вносимых параллельными пишущими транзакциями.

Механизм версий

Каждая запись в таблице может иметь несколько версий, хранящихся в отдельной области (например, в сегменте отката в Oracle или в файлах версий в PostgreSQL). Версия содержит:

  • Данные (значения полей).
  • Временную метку начала (или идентификатор транзакции, создавшей версию).
  • Временную метку окончания (или идентификатор транзакции, сделавшей версию неактуальной).

При чтении СУБД выбирает версию, для которой временная метка начала меньше или равна времени чтения, а временная метка окончания больше времени чтения (или равна бесконечности). Это гарантирует, что транзакция видит только те версии, которые были зафиксированы до её начала.

Управление блокировками

В классическом MVCC блокировки на запись применяются только на момент создания новой версии (обычно — на уровне строки). Читающие транзакции не блокируются пишущими, и наоборот. Однако для предотвращения конфликтов при одновременной записи в одну и ту же строку используются кратковременные блокировки (например, блокировка строки на запись в PostgreSQL). В некоторых реализациях (например, в Oracle) пишущие транзакции могут блокировать другие пишущие транзакции, но не читающие.

Очистка старых версий

Старые версии записей, которые больше не нужны ни одной активной транзакции, периодически удаляются в процессе, называемом сборкой мусора (garbage collection) или вакуумированием (vacuum). В PostgreSQL эту функцию выполняет фоновый процесс autovacuum, который автоматически определяет, какие версии можно безопасно удалить. В Oracle старые версии хранятся в сегменте отката и перезаписываются по мере необходимости.

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

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

  • Высокая производительность при чтении: читающие транзакции не блокируются пишущими, что позволяет выполнять множество параллельных запросов без ожидания.
  • Изоляция транзакций: обеспечивается уровень изоляции Snapshot Isolation (SI) или Repeatable Read (RR) без использования блокировок на чтение.
  • Отсутствие взаимоблокировок (deadlocks): в чистом MVCC взаимоблокировки возможны только при попытке одновременной записи в одни и те же данные, но они редки.
  • Поддержка длительных транзакций: транзакция может читать согласованный снимок данных, не влияя на работу других пользователей.

Недостатки

  • Увеличение объёма хранимых данных: хранение нескольких версий одной записи приводит к росту базы данных.
  • Накладные расходы на сборку мусора: процесс очистки старых версий требует ресурсов процессора и дискового ввода-вывода.
  • Сложность реализации: MVCC требует тщательного управления временными метками, версиями и блокировками, что усложняет код СУБД.
  • Проблемы с производительностью при высокой конкуренции на запись: при большом количестве одновременных изменений одной строки может возникнуть «шторм версий» (version bloat), требующий интенсивной сборки мусора.

Применение

MVCC является стандартным методом управления параллельным доступом в большинстве современных реляционных СУБД, а также во многих NoSQL-системах и системах управления версиями.

Реляционные СУБД

  • PostgreSQL: использует MVCC с версиями строк, хранящимися в таблицах и индексах. Вакуумирование — обязательный процесс для поддержания производительности.
  • Oracle Database: реализует MVCC через сегменты отката (undo segments). Читающие транзакции видят данные на момент начала запроса.
  • MySQL (InnoDB): использует MVCC для обеспечения уровней изоляции Repeatable Read и Read Committed. Версии строк хранятся в откатном логе (undo log).
  • Microsoft SQL Server: поддерживает MVCC в режиме READ_COMMITTED_SNAPSHOT и SNAPSHOT ISOLATION.
  • SQLite: реализует упрощённый MVCC, где каждая транзакция создаёт новую копию файла базы данных.

NoSQL-системы

  • MongoDB: использует MVCC для поддержки транзакций (с версии 4.0). Версии документов хранятся в журнале операций (oplog).
  • CouchDB: базируется на MVCC, где каждая запись имеет уникальный идентификатор версии, а конфликты разрешаются вручную или автоматически.
  • Apache Cassandra: реализует MVCC на уровне ячеек (cell-level) с использованием временных меток для разрешения конфликтов.

Файловые системы и системы управления версиями

  • Git: распределённая система управления версиями, в основе которой лежит MVCC: каждый коммит создаёт новую версию файлов, а старые версии сохраняются.
  • ZFS: файловая система с поддержкой снимков (snapshots), которые реализуются через механизм, аналогичный MVCC.
  • Btrfs: использует копирование при записи (copy-on-write), что является частным случаем MVCC.

Сравнение с другими методами

Блокировки (Pessimistic Concurrency Control)

В традиционном управлении конкурентным доступом с блокировками транзакции блокируют строки или таблицы при чтении и записи, что может приводить к взаимоблокировкам и снижению производительности при высокой конкуренции. MVCC позволяет избежать блокировок на чтение, но требует больше ресурсов для хранения версий.

Оптимистическое управление (Optimistic Concurrency Control)

При оптимистическом управлении транзакции выполняются без блокировок, а перед фиксацией проверяется, не изменились ли данные. В случае конфликта транзакция откатывается и повторяется. MVCC можно рассматривать как разновидность оптимистического управления, где конфликты разрешаются на уровне версий, а не повторением транзакций.

Интересные факты

  • Термин «многоверсионное управление конкурентным доступом» был введён в 1981 году в работе Филипа Бернштейна, Натана Гудмана и Васоса Хадзилакоса «Concurrency Control in Distributed Database Systems».
  • В PostgreSQL MVCC реализован без использования блокировок на чтение, что делает его одной из самых популярных СУБД для систем с высокой нагрузкой на чтение.
  • В Oracle Database MVCC позволяет читать данные без блокировок даже при выполнении длительных транзакций, что является ключевым преимуществом для систем реального времени.
  • В SQLite MVCC реализован через копирование всей базы данных при каждой транзакции, что делает его менее производительным для многопользовательских сценариев, но обеспечивает высокую надёжность.

Источники

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →