MVCC

MVCC (Multi-Version Concurrency Control) — принцип конкурентного доступа: каждая запись хранит несколько версий. Читатели не блокируют писателей, писатели не блокируют читателей. Основа PostgreSQL, InnoDB (MySQL), Oracle, CockroachDB, MongoDB.

Идея

# без MVCC (2PL locking)
Читатель ждёт пока писатель закончит.
Писатель ждёт пока читатели закончат.
→ contention, deadlocks

# с MVCC
Каждая запись имеет версии:
  row_v1 (created_by tx=10, deleted_by tx=15)
  row_v2 (created_by tx=15, deleted_by NULL)

Читатель tx=12 видит row_v1 (v2 ещё не был закоммичен на момент tx=12)
Писатель tx=15 создал row_v2 (первый — не блокирует)

Snapshot isolation

Транзакция получает snapshot базы на момент начала. Все SELECT в транзакции видят одно и то же состояние. Даже если параллельно кто-то закоммитил.

PostgreSQL реализация

# каждая tuple имеет
xmin  — tx что создал
xmax  — tx что удалил (или NULL)

# видимость для tx N:
1. xmin закоммичен и xmin < N
2. И (xmax IS NULL или xmax НЕ закоммичен или xmax > N)

Старые версии не удаляются сразу — их убирает VACUUM.

InnoDB (MySQL)

Undo log вместо inline-версий. Актуальная строка в таблице. Старые версии реконструируются по undo log обратными операциями. Быстрее для UPDATE, медленнее для long-running SELECT.

Плюсы MVCC

Минусы

Write skew (не защищает всё)

Snapshot isolation ≠ serializable. Классический пример: write skew — две транзакции читают одни данные, пишут не пересекающиеся, но нарушают инвариант.

# В любой момент должен быть хотя бы 1 доктор on-call
T1: SELECT COUNT(*) → 2, UPDATE me SET on_call = false
T2: SELECT COUNT(*) → 2, UPDATE her SET on_call = false
# оба коммитятся — 0 докторов on-call

Решение: SERIALIZABLE или SELECT FOR UPDATE.

См. также

← на главную