Базы данных
MVCC: как Postgres раздаёт согласованные снимки
Пятиминутный аналитический запрос и тысячи транзакций оформления заказов идут параллельно. Ни один не блокирует другой. Как Postgres это делает?
Что делает MVCC
MVCC хранит историю каждой строки так, чтобы каждая транзакция видела стабильную картину базы, не блокируя строки, которые трогают другие.
Без MVCC пятиминутный аналитический запрос замораживал бы каждый входящий заказ, а твой дашборд либо врал бы про итоги, либо блокировал кнопку оформления.
Метафора
Представь шумную библиотеку, где у каждой книги много копий, помеченных датой, когда их взяли с полки. Когда Свен просит «каталог на момент, когда я начал читать», библиотекарь выдаёт ему помеченную копию, замороженную на ту минуту — даже если Отто в это же время за стойкой сшивает новое издание.
Свен спокойно читает свою копию. Отто добавляет новое издание. Никто не ждёт никого; оба смотрят на разные датированные копии одной полки.
Позже уборщик проходит и выкидывает копии, которые никто больше не читает. Эта уборка — то, что Postgres называет VACUUM. Уборщик выбрасывает копию только тогда, когда ни один читатель её больше не держит.
Свен и Отто
Свен — сервер приложения, запустивший месячный отчёт. Отто — база с тысячами заказов в секунду.
Без MVCC отчёт либо блокирует orders (заморозив оформление), либо видит недописанные обновления. С MVCC Свен получает snapshot на момент старта отчёта; новые заказы идут в свежие версии, невидимые Свену. Отчёт заканчивается, оформление не зависает.
Одна ловушка, которую MVCC не решает
Две вкладки редактируют один профиль. Вкладка A загрузила, ты печатаешь. B загрузила, изменила одно поле, сохранила. Сохраняешь A — правка B молча исчезла. Это lost update.
MVCC сам по себе не лечит — попроси базу (SELECT FOR UPDATE или строже уровень изоляции).
Как это выглядит на диске
| Без MVCC | С MVCC |
|---|---|
| Строка перезаписывается на месте | Старая версия остаётся, новая добавляется рядом |
| Читатель ждёт писателя | Читатель видит старую версию, не ждёт |
| Откат — перезапись обратно | Откат — просто игнорировать новую версию |
| Нет истории для точки восстановления | Snapshot транзакции всегда согласован |
Проверь себя
Что на самом деле делает MVCC?
Долгий аналитический запрос и активный поток оформления заказов идут одновременно. Что произойдёт с MVCC?
Поставь в правильном порядке жизненный цикл одного обновления строки:
- 1 Транзакция A начинается; получает snapshot с тегом своего transaction id
- 2 Транзакция A читает строку в версии v1 — версии, помеченной до старта A
- 3 Транзакция B обновляет строку, создавая версию v2 с тегом transaction id B
- 4 Транзакция B коммитит — v2 теперь видна для новых снимков
- 5 Транзакция A всё ещё читает v1, потому что её snapshot разрешает именно это
- 6 Когда v1 больше никому не нужна, VACUUM помечает её слот переиспользуемым
Заполни пропуск: MVCC — это библиотека, где каждый читатель получает датированную _______ вместо самой книги с полки.
- 01Одним предложением: почему пятиминутный аналитический запрос в Postgres не блокирует записи оформления заказов?
- 02Lost update — это аномалия, которую MVCC решает автоматически, или её должно решать приложение?
- 03Зачем нужен VACUUM, если MVCC уже выдаёт согласованные данные без него?
- MVCC хранит несколько версий каждой строки; читатели и писатели никогда не блокируют друг друга
- Каждая транзакция получает snapshot — замороженную картину базы на момент её старта
UPDATEсоздаёт новую версию,DELETEтолько помечает старую мёртвой — физического удаления нет- Мёртвые версии накапливаются;
VACUUMпомечает их переиспользуемыми (не возвращает диск ОС) - Lost update MVCC не решает — нужен
SELECT FOR UPDATEили строжайший уровень изоляции
встречается в147
- Почему GraphQL получает N+1junior
- Механика DataLoader: батчинг на границе тикаmiddle
- Контракты batch-функции: порядок, формы, ошибкиmiddle
- Federation и lookahead: батчинг за пределами DataLoadermiddle
- Защита сложности запросов: depth, cost, persisted queriesmiddle
- Senior GraphQL API: scheduling-контракт, изоляция арендаторов, наблюдаемостьsenior
- Зачем идемпотентность: безопасные retryjunior
- Серверный state machine: четыре состояния idempotency keymiddle
- Outbox и inbox: effectively-once через dual-write границуmiddle
- Конкурентность и архитектура кеша для идемпотентности на масштабеsenior
- Наблюдаемость, production-инциденты и дизайн для глобального масштабаsenior
- Event loop: один поток, три очередиjunior
- Задачи, микрозадачи и scheduler.yield()middle
- Голодание микрозадач, длинные задачи и LoAFsenior
- Event loop Node.js: фазы, nextTick и задержка циклаsenior
- React, Vue и наблюдаемость INP в продакшенеsenior
- Render pipeline: шесть стадий от байтов до пикселейjunior
- Цена стадий и модель процесса рендерераmiddle
- Инвалидация, dirty-биты и containmiddle
- Слои композитора: продвижение, перекрытие и память GPUmiddle
- Флейм-стрип DevTools и жизненный цикл кадраmiddle
- Layout thrash: форсированная синхронная компоновкаsenior
- BeginMainFrame, анимации на потоке compositor и память GPUsenior
- Observability в проде: LoAF, INP и полная поверхность атакиsenior
- Что такое V8 и почему производительность различается в 100 разjunior
- Четырёхуровневый JIT-конвейер V8 и профилированная тиеризацияmiddle
- Hidden classes, деревья переходов и расположение в памятиmiddle
- Inline caches, состояния IC и деоптимизацияmiddle
- Orinoco GC: параллельный scavenger, конкурентная разметка и барьеры записиmiddle
- Спекулятивный движок TurboFan и ловушка deopt-loopsenior
- V8 в production: Isolates, сжатие указателей и реальные аварииsenior
- Жизненный цикл service worker и стратегии кешированияmiddle
- Граничные случаи service worker: version skew, долговременность и ловушка навигацииsenior
- Что делает реконсилер: render vs commitjunior
- Объект fiber и дерево с двойной буферизациейmiddle
- Чистота фазы render и подшаги фазы commitmiddle
- Реконсиляция: эвристики диффа и ловушка ключейmiddle
- Приоритетные lanes, time-slicing и useTransitionmiddle
- Bailout, мемоизация и tearingsenior
- React Profiler, компилятор и продакшн-наблюдаемостьsenior
- Стратегии рендеринга: SSG, SSR, ISR, streaming и гидратацияjunior
- SSG, SSR, ISR, streaming и RSC — как работает каждая стратегияmiddle
- Цена гидратации: selective, progressive, острова, resumabilitymiddle
- Hydration mismatch: причины, обнаружение и правило детерминизмаsenior
- RSC, стратегия на маршрут и production-наблюдаемостьsenior
- Core Web Vitals: что измеряют LCP, INP и CLSjunior
- CLS: почему происходят сдвиги лейаута и как их остановитьmiddle
- Трейдоффы метрик, RUM-атрибуция и цикл CI+полеsenior
- Общая картина: от URL до LCP до INP как эстафетаjunior
- Восемь слоёв трассировки: от service worker до второй навигацииmiddle
- Пять канонических поломок: где производство стабильно ломаетсяsenior
- Метод трёх треков: чтение трасс и построение системы мониторингаsenior
- Что такое cache stampede и почему он делает всё хужеjunior
- Лок и single-flight: ограничение параллельных rebuildmiddle
- XFetch: вероятностное раннее истечение без координацииmiddle
- Stale-while-revalidate и CDN request coalescingmiddle
- Детектирование stampede и дизайн TTL для продакшенаmiddle
- Метастабильный сбой, fencing-токены и production-постмортемыsenior
- Роли Raft, term и почему majority-кворум предотвращает split brainjunior
- Как Raft реплицирует log entry и решает, что его безопасно коммититьmiddle
- Выборы лидера в Raft: таймауты, правила голосования и четыре свойства безопасностиmiddle
- Raft в реальном мире: partition, медленный диск и клиентская маршрутизацияmiddle
- Расширения Raft: pre-vote, learner, snapshot и линеаризуемые чтенияsenior
- Raft в production: membership change, Multi-Raft и observabilitysenior
- Где происходит data fetching — и почему это решает LCPjunior
- Fetch waterfall''''ы — диагностика и лечение через Promise.allmiddle
- React Server Components и Suspense streamingmiddle
- Клиентский кэш: TanStack Query, SWR и stale-while-revalidatemiddle
- LCP, prefetch и race conditions в интерактивном fetchingmiddle
- Senior internals: RSC payload, слои кэша и production паденияsenior
- Трёхстороннее рукопожатие TCPjunior
- Номера последовательности и состояние соединенияmiddle
- DNS: что делает и зачем существуетjunior
- Обход резолвера: перенаправления, типы записей и gluemiddle
- TTL, кеширование и распространение DNSmiddle
- Рукопожатие за 1 RTT: key share и ECDHEmiddle
- Возобновление сессии и 0-RTTmiddle
- HTTP: язык запрос-ответ в вебеjunior
- HTTP/2: потоки, фреймы и HPACKmiddle
- HTTP/3 и QUIC: изоляция потерь на уровне потокаmiddle
- HTTP/3 в продакшне: внутренности QUIC, fallback и наблюдаемостьsenior
- HTTP дизайн: приоритеты, WebTransport и семантическая корректностьsenior
- WebSocket: HTTP-апгрейд до постоянного соединенияjunior
- Формат WebSocket-фрейма: opcodes, маскирование, фрагментацияmiddle
- Backpressure в WebSocket: когда клиенты не успеваютmiddle
- Реконнект: jittered backoff, thundering herd, восстановление сообщенийsenior
- WebSocket в масштабе: HTTP/2 мультиплексирование, permessage-deflate, C10Msenior
- WebSocket в production: прокси, безопасность и распределённая архитектураsenior
- Что делают обратные проксиjunior
- Health checks, connection draining и slow startmiddle
- Session affinity, consistent hashing и правильное решениеmiddle
- Retry-бури, circuit breakers и load sheddingsenior
- Устойчивая архитектура LB: anycast, zone-aware маршрутизация и observabilitysenior
- Почему QUIC, а не TCP+TLSjunior
- QUIC-потоки и head-of-line blockingjunior
- Connection ID и миграция сетиmiddle
- Возобновление 0-RTT и шифрование пакетовsenior
- DDoS: что это и почему работаетjunior
- Атаки усиления и истощение состоянияmiddle
- Ограничение скорости: алгоритмы и архитектураmiddle
- WAF, межсетевые экраны, mTLS и HSTSmiddle
- Отравление DNS-кэша и BGP-перехватsenior
- Эшелонированная защита и экономика атакsenior
- DNS, TCP, TLS по очереди: куда уходят миллисекундыmiddle
- Критический путь рендеринга и Core Web Vitalsmiddle
- Перехват прокси и шлюзы безопасности: rate limiter, WAF, mTLSmiddle
- Альтернативные пути: QUIC 0-RTT, WebSocket upgrade, миграция соединенияmiddle
- Наблюдаемость: распределённые трейсы, USE/RED и семплированиеsenior
- Устойчивость: каскадные повторы, circuit breakers и error budgetsenior
- Что такое три сигнала: метрики, логи, трейсыjunior
- Зачем нужны структурные логи: дневник против таблицыjunior
- Схема продакшн-лога: поля, которые несёт каждая строкаmiddle
- PII-редакция и log injectionsenior
- OTel Logs Data Model и audit-логи как подсистемаsenior
- SLI, SLO и error budget: надёжность в числахjunior
- Error budget policy, latency SLO и составные journeysmiddle
- Продакшн-отказы SLO, самонаблюдаемость, безопасность и общая картинаsenior
- Петля инцидента: от пейджера до постмортема до предотвращенияmiddle
- Cache lines и false sharing: когда параллелизм замедляет кодmiddle
- SIMD и data layout: AoS vs SoA и разница в 4–8xmiddle
- Cache-oblivious алгоритмы, PGO и production failuressenior
- GC в production: наблюдаемость, безопасность, edge cases и управление флотомsenior
- Batching: амортизируй фиксированную цену каждой операцииjunior
- Окно батчинга: размер и время ожиданияmiddle
- Batching в Kafka и Postgresmiddle
- io_uring и наблюдаемость пакетированияmiddle
- От Nagle до io_uring: эволюция пакетированияmiddle
- Backpressure, изоляция сбоев и безопасность батчей в продакшенеsenior
- CI enforcement и RUM: делаем бюджеты рабочимиmiddle
- V8 JIT-пайплайн, HTTP-приоритеты и безопасность bundlesenior
- Цикл performance: дисциплина, а не проектjunior
- Классификация и исправление: сопоставление family bottleneck с методамиmiddle
- Observability-стек и CI gates: ловить регрессии до выпускаmiddle
- От инцидента к enforcement: SLO burn до верифицированного исправления за 35 минутmiddle
- Культура, экономика и масштаб performancesenior
- At-most-once, at-least-once, exactly-once: три контракта доставкиjunior
- Три ножки сбоя — где реально происходят дубликаты и потериmiddle
- Consumer-side dedup: самый дешёвый путь к exactly-once processingmiddle
- Kafka exactly-once semantics: idempotent producer и транзакцииmiddle
- SQS visibility timeout, DLQ и outbox patternmiddle
- Exactly-once в production: impossibility-доказательство, гибридные паттерны и реальные инцидентыsenior
- Что такое OAuth и почему пароли — не ответjunior
- Authorization code flow с PKCEmiddle
- Валидация ID-токена и управление JWKS-кешемmiddle
- Ротация refresh-токенов и scope-based least privilegemiddle
- Sender-constrained токены: DPoP и mTLSsenior
- OAuth в production: audience атаки, observability и реальные провалыsenior