Почему в PostgreSQL не поддерживается уровень изоляции READ UNCOMMITTED?

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

Ответ

PostgreSQL не реализует уровень изоляции READ UNCOMMITTED (чтение незафиксированных данных) из-за своей архитектуры MVCC (Multiversion Concurrency Control). В этой модели каждый запрос видит снимок данных (snapshot) на момент своего начала, что физически исключает возможность чтения "грязных" данных из незавершённых транзакций.

Как это работает:

  • При любом SELECT система обращается к согласованному снимку, а не к непосредственно изменяемым страницам данных.
  • Если вы явно укажете SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;, PostgreSQL тихо понизит его до READ COMMITTED (чтение зафиксированных данных) — минимального доступного уровня.

Пример на C# с Npgsql:

using var connection = new NpgsqlConnection(connectionString);
await connection.OpenAsync();

// Попытка установить ReadUncommitted
using var transaction = await connection.BeginTransactionAsync(
    IsolationLevel.ReadUncommitted // Будет проигнорирован
);

var command = new NpgsqlCommand("SELECT balance FROM accounts WHERE id = 1", connection, transaction);
var balance = await command.ExecuteScalarAsync();
// Здесь balance будет взят из снимка READ COMMITTED,
// а не из незавершённой транзакции другого пользователя.

Почему это осознанный выбор:

  1. Целостность данных: Гарантируется, что приложение никогда не прочитает данные, которые могут быть откатаны, что предотвращает логические ошибки.
  2. Производительность: MVCC позволяет выполнять чтение без блокировок, даже при наличии параллельных пишущих транзакций, что хорошо для OLTP-нагрузки.
  3. Упрощение модели: Разработчикам не нужно думать о последствиях чтения "грязных" данных, так как этот сценарий невозможен.

Если вам нужно читать данные без ожидания завершения других транзакций, в PostgreSQL для этого используется уровень READ COMMITTED в сочетании с MVCC.