Без многоверсионности согласованное чтение требует блокировок: пока транзакция читает, писатель ждёт, и наоборот. MVCC снимает это противоречие — у каждой транзакции своя точка зрения на данные.
Как это устроено в PostgreSQL:
- каждая версия строки (tuple) хранит
xmin — транзакцию, которая её создала, и xmax — транзакцию, которая её удалила или заменила; UPDATE не перезаписывает строку: он ставит xmax старой версии и вставляет новую с xmin = текущая транзакция;- снимок — список транзакций, которые на момент его взятия уже завершились; версия видна, если её
xmin подтверждён в снимке, а xmax — нет; - в Read Committed снимок берётся на каждый запрос, в Repeatable Read — один на всю транзакцию.
Что даёт: длинный отчёт видит согласованное состояние базы на момент начала и никого не блокирует. Писатели блокируют друг друга только на одной и той же строке — второй ждёт коммита или отката первого.
Цена — старые версии. Они нужны, пока жива транзакция, которая может их увидеть. Убирает их VACUUM (autovacuum в фоне): освобождает место под новые версии. Проблемы:
- раздувание (bloat) — частые UPDATE при отстающем автовакууме;
- долгая транзакция или забытая
idle in transaction держит горизонт: ни одна версия новее её начала не может быть убрана во всей базе. Таблицы пухнут, запросы замедляются. Ищут через pg_stat_activity, ставят idle_in_transaction_session_timeout; - переполнение счётчика транзакций — 32-битные номера, нужна заморозка (freeze) старых строк, иначе база остановится для защиты.
Другие реализации. InnoDB и Oracle меняют строку на месте, а прошлую версию кладут в undo-журнал; читатель со старым снимком восстанавливает нужную версию из undo. Уборка — purge-поток. Плюс: таблица не пухнет; минус: долгий снимок раздувает undo и замедляет чтения.
MVCC и аномалии. Снимок защищает от грязного и неповторяемого чтения, но не от всех аномалий: на Repeatable Read (Snapshot Isolation) возможна асимметрия записи (write skew). Полную сериализуемость PostgreSQL даёт уровнем Serializable (SSI) — с откатом транзакций при конфликте.