Ответ
MVCC (Multi-Version Concurrency Control) — это основной механизм PostgreSQL для управления одновременным доступом к данным. Его ключевое преимущество — чтение данных никогда не блокирует запись, и наоборот.
Принцип работы
Вместо использования блокировок для каждой операции чтения, MVCC работает с версиями строк:
- Создание "снимка" данных: Каждая транзакция в момент своего старта получает "снимок" (snapshot) базы данных. Она видит только те данные, которые были зафиксированы (committed) до её начала.
- Версионирование строк:
- При
UPDATEстроки старая версия не удаляется, а помечается как неактуальная. Рядом создается новая версия этой же строки. - При
DELETEстрока также не удаляется физически, а лишь помечается как удаленная для будущих транзакций.
- При
- Изоляция транзакций: Активные транзакции продолжают работать со своими "снимками", игнорируя изменения, сделанные в других параллельных транзакциях, до тех пор, пока те не будут зафиксированы.
- Очистка: Фоновый процесс
VACUUMпериодически удаляет старые, уже невидимые ни для одной активной транзакции версии строк, освобождая место.
Пример
-- Исходное состояние: в таблице есть пользователь с именем 'Bob'
-- balance = 100
-- Транзакция 1 (началась в 10:00)
BEGIN;
SELECT balance FROM accounts WHERE user_id = 1; -- Видит balance = 100
-- Транзакция 2 (началась в 10:01, параллельно)
BEGIN;
UPDATE accounts SET balance = 150 WHERE user_id = 1;
COMMIT; -- Изменение зафиксировано
-- Транзакция 1 все еще работает со своим старым "снимком"
SELECT balance FROM accounts WHERE user_id = 1; -- По-прежнему видит balance = 100
COMMIT;
-- Новая транзакция, начатая после 10:01, увидит обновленное значение
SELECT balance FROM accounts WHERE user_id = 1; -- Увидит balance = 150
Этот подход обеспечивает высокую производительность в системах с большим количеством одновременных чтений и записей.
Ответ 18+ 🔞
А, MVCC, ёпта, это же та самая магия, которая в Постгресе позволяет не устраивать драку за данные! Чтоб никто никого не блокировал почём зря. Представь себе библиотеку, где каждый читатель приходит и получает свою личную, замороженную во времени копию книги, а писатели в это время спокойно в соседней комнате новое издание готовят. Никто никому не мешает — красота, блядь!
Как эта штука, блядь, работает
Вместо того чтобы вешать здоровенные замки на каждую строчку, Постгрес просто плодит версии данных, как сумасшедший. Вот смотри:
- "Снимок" на память: Каждая транзакция, только начавшись, делает моментальный снимок всей базы. И дальше она живёт в этой своей реальности, как в матрице, и видит только те данные, которые были закоммичены до её старта. Всё, что происходит параллельно — её не колышет, пока это не станет официальной историей.
- Версионирование — наше всё:
- Делаешь
UPDATE? Старая версия строки не стирается, она просто становится призраком для новых транзакций. Рядом лепится её свежая, обновлённая копия. - Удаляешь строку
DELETE? Она физически никуда не девается! Просто на неё вешают табличку "удалена" для всех, кто придёт после.
- Делаешь
- Изоляция, блядь, полная: Пока ты в транзакции, ты в своём мирке. Другие могут рядом творить что угодно — добавлять, менять, удалять. Ты этого не увидишь и не почувствуешь, пока не закончишь свою работу. Никаких грязных чтений, ёпта!
- Уборщик
VACUUM: А иначе что? Эти версии строк будут копиться, как хлам в квартире распиздяя. Поэтому фоновый процессVACUUMработает, как дворник — выгребает старые, уже никому не нужные версии, которые не видны ни одной живой транзакции. Освобождает место, чтоб не захламлялось.
Пример, чтобы совсем пиздец стало понятно
-- Допустим, у нас Вася, и у него на счету 100 рублей. Всё честно.
-- Транзакция 1 (зашла в систему в 10:00, как штык)
BEGIN;
SELECT balance FROM accounts WHERE user_id = 1; -- Видит свои честные 100 рублей. Спокойно.
-- Транзакция 2 (вломилась в 10:01, пока первая думает)
BEGIN;
UPDATE accounts SET balance = 150 WHERE user_id = 1; -- Банкомат сломался, начислил лишних 50!
COMMIT; -- Идиот, закоммитил это безобразие!
-- А наша первая транзакция всё ещё в своей старой реальности сидит, как будто ничего не было!
SELECT balance FROM accounts WHERE user_id = 1; -- Всё так же видит свои 100 рублей. Спокойствие, только спокойствие.
COMMIT;
-- А вот новая транзакция, которая стартанёт после всей этой ебалы, увидит уже новую, испорченную реальность.
SELECT balance FROM accounts WHERE user_id = 1; -- Опа, 150! Где справедливость?
Вот так вот, сука. Этот подход — просто овердохуища для систем, где нужно, чтобы куча народа одновременно и читала, и писала, не устраивая при этом давку и мордобой. Все довольны, все при своих снимках данных. Главное потом VACUUM не забывать запускать, а то место кончится, блядь.