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

Как работает транзакция?

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

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

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

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

Как работают транзакции

Транзакция — это неделимая последовательность операций БД, которая либо полностью выполняется (COMMIT), либо полностью отменяется (ROLLBACK).

ACID свойства

Atomicity — либо все, либо ничего. Если во время выполнения происходит ошибка, все изменения откатываются.

Consistency — база переходит из одного консистентного состояния в другое. Все constraints соблюдены.

Isolation — транзакции выполняются независимо, не видят промежуточные изменения друг друга.

Durability — после коммита данные сохранены на диск и не теряются при сбое.

Пример на Python

import psycopg2

conn = psycopg2.connect("dbname=bank")
cursor = conn.cursor()

try:
    cursor.execute("BEGIN")
    cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
    cursor.execute("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
    conn.commit()  # Обе операции фиксируются атомарно
except Exception as e:
    conn.rollback()  # Отменяем все
    print(f"Ошибка: {e}")
finally:
    cursor.close()
    conn.close()

Уровни изоляции

  1. READ UNCOMMITTED — видим грязные чтения (незакомиченные данные)
  2. READ COMMITTED — видим только закомиченные данные (default PostgreSQL)
  3. REPEATABLE READ — стабильное чтение в пределах транзакции
  4. SERIALIZABLE — максимальная изоляция, как будто транзакции выполняются последовательно
cursor.execute("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE")

Блокировки

Shared Lock (FOR SHARE) — несколько процессов читают одновременно

BEGIN;
    SELECT * FROM accounts WHERE id = 1 FOR SHARE;
COMMIT;

Exclusive Lock (FOR UPDATE) — только один процесс работает

BEGIN;
    SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
    UPDATE accounts SET balance = 1000 WHERE id = 1;
COMMIT;

Savepoints — частичный откат

cursor.execute("BEGIN")
cursor.execute("UPDATE accounts SET balance = 1000 WHERE id = 1")
cursor.execute("SAVEPOINT sp1")
cursor.execute("UPDATE accounts SET balance = -500 WHERE id = 2")

try:
    conn.commit()
except:
    cursor.execute("ROLLBACK TO sp1")  # Откат только до savepoint
    conn.commit()

Deadlock

Взаимная блокировка: процесс 1 ждет ресурс процесса 2, процесс 2 ждет ресурс процесса 1.

Решение: всегда берите блокировки в одинаковом порядке.

SQLAlchemy пример

from sqlalchemy.orm import Session

with Session(engine) as session:
    try:
        account1 = session.query(Account).get(1)
        account1.balance -= 100
        account2 = session.query(Account).get(2)
        account2.balance += 100
        session.commit()  # Атомарный коммит
    except:
        session.rollback()  # Откат всех изменений
        raise

Транзакция обеспечивает целостность и надежность данных при параллельном доступе множества пользователей.