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

Ответ

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.

Ответ 18+ 🔞

А, слушай, про PostgreSQL и этот их READ UNCOMMITTED — это вообще отдельная песня, блядь. Так вот, сука, они его в принципе не делают, нахрен не нужен. А почему? Да потому что у них там архитектура, ёпта, MVCC, то есть многоверсионность, блядь. Это ж гениальная штука, если вдуматься.

Представь себе: каждый твой запрос, когда начинается, он как бы фотографирует данные на этот самый момент. Снимок, snapshot, называется. И работает он уже с этой фоткой, а не лезет в живую, сука, базу, где прямо сейчас кто-то пишет, удаляет или хуй знает что творит. Поэтому физически, блядь, прочитать какие-то незавершённые, «грязные» данные — это просто невозможно, как ни старайся. Их нет в этом снимке, они в другом измерении, в рот меня чих-пых!

И вот самый прикол: если ты такой умный и попробуешь в коде явно указать «дайте мне READ UNCOMMITTED», типа:

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

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

А почему они так сделали? Да потому что нехуй, вот почему! Во-первых, целостность данных, блядь. Чтобы твоё приложение не прочитало какую-нибудь хуйню, которую другая транзакция через секунду откатит, и ты потом с этими данными начнёшь делать какую-то ересь. Во-вторых, производительность. Чтение без блокировок — это же красота! Один пишет, десять читают, и никто никого не ждёт. И в-третьих, простота, ёпта. Разработчику не надо голову ломать, думать: «А не прочту ли я сейчас какую-нибудь дичь?» Не прочтёшь. Расслабься.

Так что если тебе нужно, чтобы SELECT не вис, ожидая, пока кто-то там COMMIT сделает, то в Постгресе и так по умолчанию READ COMMITTED с его MVCC эту проблему решает. Всё уже придумано до нас, мудаков.