Multi-Version Concurrency Control
MVCC (Multi-Version Concurrency Control, много版本ное управление конкурентным доступом) — это механизм управления параллельным доступом к данным в системах управления базами данных (СУБД), который позволяет нескольким транзакциям одновременно читать и записывать данные, не блокируя друг друга. В основе MVCC лежит принцип создания и хранения нескольких версий (снимков) одной и той же строки данных, что обеспечивает изоляцию транзакций и повышает производительность системы в условиях высокой нагрузки.
История
Концепция MVCC была впервые предложена в 1970-х годах в рамках исследований по созданию распределённых баз данных. Ранние реализации появились в академических проектах, таких как System R (IBM) и Ingres (Калифорнийский университет в Беркли). Однако широкое распространение механизм получил в 1990-х годах с развитием реляционных СУБД, ориентированных на высокую производительность и масштабируемость.
Первой коммерческой СУБД, внедрившей MVCC, стала Oracle (версия 7, 1992 год). Впоследствии механизм был реализован в PostgreSQL (с версии 6.5, 1999 год), MySQL (с версии 5.0, 2005 год, в движке InnoDB), Microsoft SQL Server (с версии 2005, в режиме snapshot isolation) и других системах. В настоящее время MVCC является стандартом де-факто для большинства современных реляционных и нереляционных СУБД, включая MongoDB, CockroachDB и FoundationDB.
Принцип работы
MVCC решает проблему конфликтов между параллельными транзакциями, которые могут одновременно читать и изменять одни и те же данные. Вместо того чтобы блокировать строку при её изменении (как в традиционных блокирующих протоколах), MVCC создаёт новую версию строки при каждой операции записи. Каждая версия помечается временной меткой (timestamp) или идентификатором транзакции, который определяет, когда эта версия стала видимой для других транзакций.
Создание версий
При выполнении операции UPDATE или DELETE СУБД не удаляет старую версию строки, а создаёт новую. Старая версия сохраняется в специальной области — сегменте отката (undo log) или в самой таблице (в зависимости от реализации). Каждая версия содержит:
- Данные строки (значения полей).
- Идентификатор транзакции-создателя (xmin) — ID транзакции, которая создала эту версию.
- Идентификатор транзакции-удаления (xmax) — ID транзакции, которая пометила версию как удалённую (если применимо).
Видимость версий
Транзакция, выполняющая чтение, видит только те версии строк, которые были созданы до её начала (или до момента выполнения команды, в зависимости от уровня изоляции) и не были удалены другими транзакциями. Это обеспечивается с помощью правил видимости, основанных на временных метках или идентификаторах транзакций. Например, в PostgreSQL используется механизм snapshot, который фиксирует состояние всех активных транзакций на момент начала чтения.
Очистка старых версий
Со временем старые версии строк становятся невидимыми для всех активных транзакций. Процесс, называемый сборкой мусора (garbage collection) или вакуумированием (vacuuming), периодически удаляет эти версии, освобождая место в хранилище. В PostgreSQL эту функцию выполняет фоновый процесс autovacuum, в MySQL (InnoDB) — механизм очистки пула буферов.
Уровни изоляции транзакций
MVCC поддерживает стандартные уровни изоляции, определённые в SQL-стандарте (ANSI/ISO), но с некоторыми особенностями:
- Read Uncommitted (чтение неподтверждённых данных) — транзакция может читать данные, изменённые другими транзакциями, даже если те ещё не зафиксированы. В большинстве СУБД с MVCC этот уровень фактически эквивалентен Read Committed, так как механизм версий предотвращает чтение «грязных» данных.
- Read Committed (чтение подтверждённых данных) — транзакция видит только те версии строк, которые были зафиксированы до момента выполнения текущей команды. Это наиболее распространённый уровень изоляции.
- Repeatable Read (повторяемое чтение) — транзакция видит снимок данных, зафиксированный на момент её начала. Все последующие чтения в рамках той же транзакции возвращают один и тот же набор данных, даже если другие транзакции изменяют строки.
- Serializable (сериализуемый) — самый строгий уровень, при котором транзакции выполняются так, как если бы они были последовательными. MVCC может реализовывать его с помощью дополнительных механизмов, таких как предикатные блокировки или обнаружение конфликтов (например, в PostgreSQL — Serializable Snapshot Isolation, SSI).
Преимущества и недостатки
Преимущества
- Высокая производительность при чтении — читающие транзакции не блокируются пишущими, что особенно важно в системах с большим количеством параллельных операций чтения (например, веб-приложения, аналитические системы).
- Отсутствие взаимоблокировок (deadlocks) при чтении — поскольку чтение не требует блокировок, риск взаимоблокировок снижается.
- Изоляция транзакций без блокировок — MVCC обеспечивает уровни изоляции (Repeatable Read, Serializable) без необходимости длительного удержания блокировок на строках.
- Поддержка длительных транзакций — транзакции могут выполняться длительное время, не мешая другим операциям.
Недостатки
- Увеличение объёма хранилища — хранение нескольких версий строк требует дополнительного дискового пространства. В системах с интенсивными операциями записи объём может расти быстро.
- Накладные расходы на сборку мусора — процесс очистки старых версий (вакуумирование) потребляет ресурсы процессора и может вызывать временные задержки.
- Сложность реализации — MVCC требует сложной логики управления версиями, видимостью и конкурентным доступом, что увеличивает сложность кода СУБД.
- Проблемы с фантомными чтениями — на уровне Repeatable Read MVCC не всегда предотвращает появление новых строк (фантомов), созданных другими транзакциями. Для устранения этой проблемы требуются дополнительные механизмы (например, блокировки диапазонов).
Реализации в популярных СУБД
PostgreSQL
PostgreSQL использует MVCC на основе идентификаторов транзакций. Каждая строка содержит поля xmin и xmax, определяющие, какая транзакция создала и удалила версию. Старые версии хранятся в самой таблице, а процесс autovacuum периодически удаляет их. Уровни изоляции: Read Committed (по умолчанию), Repeatable Read, Serializable.
MySQL (InnoDB)
InnoDB реализует MVCC с помощью сегментов отката (undo log). При изменении строки старая версия записывается в undo log, а новая — в таблицу. Видимость определяется на основе идентификатора транзакции и списка активных транзакций (read view). Уровни изоляции: Read Uncommitted, Read Committed (по умолчанию), Repeatable Read, Serializable.
Oracle
Oracle использует MVCC с момента версии 7. Старые версии строк хранятся в сегментах отката (undo tablespace). Транзакции видят снимок данных на момент начала запроса (read consistency). Уровень изоляции по умолчанию — Read Committed, но поддерживается и Serializable.
Microsoft SQL Server
SQL Server реализует MVCC в режиме snapshot isolation (введён в версии 2005). При включении этого режима строки хранят версии в tempdb. Уровни изоляции: Read Committed (по умолчанию, с блокировками), Read Committed Snapshot (RCSI), Snapshot, Serializable.
Применение
MVCC широко используется в системах, где требуется высокая производительность при параллельном доступе к данным:
- Веб-приложения — интернет-магазины, социальные сети, системы управления контентом (CMS), где множество пользователей одновременно читают и записывают данные.
- Финансовые системы — банковские транзакции, биржевые торги, где важна изоляция и согласованность данных.
- Аналитические системы — хранилища данных, системы бизнес-аналитики (BI), где выполняются длительные запросы на чтение при параллельной загрузке данных.
- Распределённые базы данных — системы, работающие на нескольких узлах (например, CockroachDB, Google Spanner), где MVCC помогает синхронизировать данные между репликами.
Критика
Основные критические замечания в адрес MVCC связаны с его сложностью и накладными расходами. В системах с очень высокой интенсивностью записи (например, в логовых системах) хранение множества версий может приводить к значительному росту объёма данных и замедлению сборки мусора. Кроме того, в некоторых реализациях (например, в MySQL InnoDB) при длительных транзакциях может возникнуть проблема «разрастания» undo log, что приводит к снижению производительности.
Альтернативой MVCC являются протоколы на основе блокировок (например, двухфазная блокировка, 2PL) и оптимистические методы управления конкурентным доступом (OCC), которые не требуют хранения версий, но могут быть менее эффективны при высокой конкуренции.
Источники
- Gray, J., & Reuter, A. (1993). Transaction Processing: Concepts and Techniques. Morgan Kaufmann.
- Bernstein, P. A., Hadzilacos, V., & Goodman, N. (1987). Concurrency Control and Recovery in Database Systems. Addison-Wesley.
- PostgreSQL Global Development Group. (2023). PostgreSQL Documentation: Chapter 13. Concurrency Control.
- MySQL Reference Manual. (2023). InnoDB Multi-Versioning.
- Oracle Corporation. (2023). Oracle Database Concepts: Multiversion Read Consistency.
- Microsoft. (2023). SQL Server and Azure SQL Database: Snapshot Isolation.
- Stonebraker, M. (1981). Operating System Support for Database Management. Communications of the ACM, 24(7), 412–418.
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →