Почему мультиверсионность (MVCC) мешает реализации уровня изоляции Read Uncommitted?

«Почему мультиверсионность (MVCC) мешает реализации уровня изоляции Read Uncommitted?» — вопрос из категории Базы данных, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Механизм Multiversion Concurrency Control (MVCC) и уровень изоляции READ UNCOMMITTED концептуально противоречат друг другу, что делает их совместную реализацию бессмысленной или крайне неэффективной.

Суть противоречия:

  • Цель READ UNCOMMITTED: Позволить транзакции видеть незафиксированные изменения (dirty reads) других параллельных транзакций, жертвуя целостностью данных ради максимальной скорости чтения.
  • Принцип MVCC: Каждая операция чтения (транзакция) работает с согласованным снимком данных (snapshot) на определённый момент времени (обычно на момент начала транзакции или оператора). Этот снимок содержит только те изменения, которые были уже зафиксированы к этому моменту.

Почему MVCC «мешает»? MVCC по своей природе изолирует читающую транзакцию от незавершённых изменений. Чтобы реализовать READ UNCOMMITTED поверх MVCC, системе пришлось бы:

  1. Отказаться от основного преимущества MVCC — предоставления читателям стабильного снимка без блокировок.
  2. Начать отслеживать и предоставлять доступ к незафиксированным версиям строк, что резко усложняет механизм управления версиями.
  3. По сути, внедрить дополнительный, противоречащий основной логике, механизм для поддержки поведения, которое считается антипаттерном с точки зрения целостности данных.

Техническая иллюстрация:

-- Транзакция 1 (Модификация)
BEGIN TRANSACTION; -- Создаётся новая версия строки для этой транзакции (V_new).
UPDATE Users SET Balance = 1000 WHERE Id = 1; -- V_new не зафиксирована.

-- Транзакция 2 (Чтение с уровнем READ UNCOMMITTED в системе с MVCC)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;
-- При MVCC: Транзакция 2 получит снимок на момент своего начала.
-- В снимок попадает только последняя ЗАФИКСИРОВАННАЯ версия строки (V_old).
SELECT Balance FROM Users WHERE Id = 1; -- Вернёт старое значение, а не 1000.
-- Чтобы увидеть 1000, системе пришлось бы специально искать незафиксированную V_new,
-- что нарушает изоляцию снимка и не является стандартным поведением MVCC.
COMMIT;

Вывод: Системы, основанные на чистом MVCC (как PostgreSQL, Oracle), часто просто не реализуют READ UNCOMMITTED как отдельный уровень, либо он семантически работает идентично READ COMMITTED. Реализация истинных «грязных чтений» требует подхода, основанного на блокировках (как в SQL Server), а не на версионности.