← Назад к вопросам

Как реализована надежность транзакции в postgresql?

1.8 Middle🔥 121 комментариев
#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как реализована надежность транзакций в PostgreSQL?

ПостгресQL имеет одну из самых надёжных реализаций ACID транзакций. Давайте разберёмся, как она это достигает.

1. ACID — основа надежности

ACID — это четыре свойства надёжных транзакций:

  • Atomicity (Атомарность) — транзакция либо полностью выполнится, либо полностью откатится
  • Consistency (Согласованность) — БД переходит из одного консистентного состояния в другое
  • Isolation (Изоляция) — транзакции не мешают друг другу
  • Durability (Долговечность) — данные сохранены на диск

2. WAL (Write-Ahead Logging) — сердце надежности

ПостгресQL использует WAL для гарантии durability:

Данные в памяти (Shared Buffers)
    ↓
Записываем в WAL (диск) — это происходит ДО записи данных
    ↓
Данные в памяти изменены
    ↓
CHECKPOINT периодически записывает данные на диск

Механизм:

-- BEGIN транзакция
BEGIN;

-- Каждое изменение сначала идёт в WAL
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- PostgreSQL записывает WAL record на диск

-- Данные в памяти изменены, но на диск пока не пошли

COMMIT;
-- При коммите: fsync() гарантирует, что WAL на диске
-- Теперь данные безопасны, даже если сервер упадёт

3. Уровни изоляции (Isolation Levels)

ПостгресQL реализует 4 уровня изоляции согласно SQL стандарту:

-- 1. READ UNCOMMITTED (самый слабый)
-- В PostgreSQL это тоже самое что READ COMMITTED
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 2. READ COMMITTED (по умолчанию)
-- Видит только закоммиченные данные
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

BEGIN;
SELECT balance FROM accounts WHERE id = 1;  -- Видит коммитные данные
COMMIT;

-- 3. REPEATABLE READ
-- Одна трансакция видит снимок БД на момент начала
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

BEGIN;
  SELECT balance FROM accounts WHERE id = 1;  -- Снимок 1
  -- Другая транзакция меняет данные и коммитит
  SELECT balance FROM accounts WHERE id = 1;  -- Снимок остаётся прежним
COMMIT;

-- 4. SERIALIZABLE (самый строгий)
-- Изоляция как если бы транзакции выполнялись последовательно
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

4. MVCC (Multi-Version Concurrency Control)

ПостгресQL использует MVCC для читаемости без блокировок:

Все версии данных хранятся в памяти:
- xmin — XID, который создал эту версию
- xmax — XID, который удалил эту версию

Когда транзакция читает, она видит версии на основе своего XID

Пример MVCC:

-- Окно 1: Транзакция A
BEGIN;  -- XID = 100
SELECT balance FROM accounts;  -- Видит версии где xmin <= 100 и (xmax > 100 или NULL)

-- Окно 2: Транзакция B
BEGIN;  -- XID = 101
UPDATE accounts SET balance = 500 WHERE id = 1;
-- Создаёт новую версию с xmin = 101
COMMIT;

-- Окно 1: Транзакция A
SELECT balance FROM accounts;  -- Видит старую версию (xmin = 100)
-- Видит так же как раньше, потому что данные B'а не видны для A
COMMIT;

5. Блокировки для конкурентности

ПостгресQL использует различные типы блокировок:

-- SELECT — не требует блокировки (MVCC)
SELECT * FROM accounts WHERE id = 1;

-- SELECT FOR UPDATE — эксклюзивная блокировка
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
-- Другие транзакции ждут, пока эта закончится

-- SELECT FOR SHARE — совместная блокировка
SELECT * FROM accounts WHERE id = 1 FOR SHARE;
-- Несколько транзакций могут иметь FOR SHARE

-- Deadlock detection
BEGIN;  -- Транзакция 1
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- Другая транзакция пытается
SELECT * FROM accounts WHERE id = 2 FOR UPDATE;

-- Если транзакция 2 пытается 1, а 2 пытается 1 — deadlock
-- PostgreSQL обнаружит и откатит одну из них
-- ERROR: deadlock detected

6. Механизм COMMIT

# Python пример с psycopg2
import psycopg2

conn = psycopg2.connect('dbname=test')
cur = conn.cursor()

try:
    # BEGIN транзакция
    cur.execute('UPDATE accounts SET balance = balance - 100 WHERE id = 1')
    cur.execute('UPDATE accounts SET balance = balance + 100 WHERE id = 2')
    
    # COMMIT — важный момент
    conn.commit()
    # PostgreSQL:
    # 1. Записывает COMMIT record в WAL
    # 2. Запускает fsync() чтобы убедиться WAL на диске
    # 3. Изменяет видимость для других транзакций
    
except Exception as e:
    conn.rollback()  # ROLLBACK
    # PostgreSQL откатит все изменения
    print(f'Error: {e}')
finally:
    cur.close()
    conn.close()

7. Recovery после сбоя

Сервер упал
    ↓
ПостгресQL восстанавливается из WAL
    ↓
Откатывает незакоммиченные транзакции (undo from WAL)
    ↓
Повторяет закоммиченные транзакции (redo from WAL)
    ↓
Проверяет консистентность и запускается

8. Практический пример надежности

-- Сценарий: перевод денег между счетами
BEGIN TRANSACTION;
  UPDATE accounts 
  SET balance = balance - 100 
  WHERE id = 1;
  
  -- Если произойдёт ошибка ДО следующей строки, изменение откатится
  
  UPDATE accounts 
  SET balance = balance + 100 
  WHERE id = 2;
COMMIT;  -- Обе операции коммитятся вместе

-- Если сервер упадёт:
-- - До COMMIT: обе операции откатятся из WAL
-- - После COMMIT: обе операции восстановятся из WAL
-- Никогда не будет ситуации когда отправили, но не получили!

9. Явное управление изоляцией

import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE

conn = psycopg2.connect('dbname=test')
conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)

cur = conn.cursor()
# Все транзакции будут SERIALIZABLE
cur.execute('SELECT * FROM accounts')
conn.commit()

10. Синхронная репликация

Первичный сервер
    ↓ (отправляет WAL)
Вторичный сервер (реплика)
    ↓ (получает и подтверждает)
Первичный получает confirmation
    ↓
Клиент получает COMMIT успешно

11. Проверка целостности

-- Проверить состояние транзакций
SELECT * FROM pg_stat_activity;

-- Смотреть блокировки
SELECT * FROM pg_locks;

-- Информация о WAL
SHOW wal_level;  -- replica, logical
SHOW fsync;      -- on/off

Вывод

ПостгресQL достигает надёжности через:

  1. WAL (Write-Ahead Logging) — гарантирует, что коммитные данные на диске
  2. MVCC — позволяет читать без блокировок
  3. ACID изоляция — четыре уровня для разных требований
  4. Блокировки — управляют конкурентным доступом
  5. Recovery механизм — восстанавливается после сбоев
  6. Синхронная репликация — копирует данные на другие серверы

Это делает PostgreSQL одной из самых надёжных баз данных с гарантией ACID.