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

Что такое изоляция?

2.0 Middle🔥 131 комментариев
#Другое

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

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

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

Изоляция (Isolation) в базах данных

Изоляция (Isolation) — это один из четырех ключевых принципов ACID, который гарантирует, что одновременные (конкурирующие) транзакции не влияют друг на друга и видят консистентное состояние данных. Это позволяет множеству пользователей работать с БД одновременно без конфликтов.

Проблема параллелизма

Когда несколько транзакций работают одновременно, возникают проблемы:

Транзакция 1                    Транзакция 2
├─ Читает Balance = $100
├─ Снимает $30
│                               ├─ Читает Balance = $100
│                               ├─ Снимает $20
├─ Записывает Balance = $70
│                               ├─ Записывает Balance = $80
│                               └─ COMMIT
└─ COMMIT

Результат: $80 вместо $50!
Потеряны $20 (Dirty Read, Lost Update)

Четыре уровня изоляции

ПостСКВЕЛ стандарт определяет 4 уровня возрастающей строгости:

1. READ UNCOMMITTED (читать не закоммиченные данные)

# Самый низкий уровень, почти не обеспечивает изоляцию

# Проблемы:
Тр1: UPDATE account SET balance = 50 WHERE id = 1
     (не коммитита)
Тр2: SELECT balance FROM account WHERE id = 1  # Видит 50
Тр1: ROLLBACK  # Откатывается
Тр2: уже прочитала несуществующее значение (Dirty Read)

# Когда использовать:
# - Очень редко, только для статистики
# - При необходимости максимальной производительности

import psycopg2
conn = psycopg2.connect()
conn.set_isolation_level(0)  # READ UNCOMMITTED

2. READ COMMITTED (читать только закоммиченные данные)

# По умолчанию в большинстве БД

# Защита от:
# ✅ Dirty Read

# Остается уязвимость к:
# ❌ Non-Repeatable Read
# ❌ Phantom Read

# Пример Non-Repeatable Read:
Тр1: SELECT salary FROM employee WHERE id = 5  # $50000
     (пауза)
Тр2: UPDATE employee SET salary = 60000 WHERE id = 5
Тр2: COMMIT
Тр1: SELECT salary FROM employee WHERE id = 5  # $60000 (!)
     # Одно и то же поле — разные значения

# В PostgreSQL:
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)

3. REPEATABLE READ (повторяемое чтение)

# Средний уровень изоляции

# Защита от:
# ✅ Dirty Read
# ✅ Non-Repeatable Read

# Остается уязвимость к:
# ❌ Phantom Read (добавление новых строк)

# Пример Phantom Read:
Тр1: SELECT COUNT(*) FROM orders WHERE status = 'pending'  # 3
     (пауза)
Тр2: INSERT INTO orders (status) VALUES ('pending')
Тр2: COMMIT
Тр1: SELECT COUNT(*) FROM orders WHERE status = 'pending'  # 4
     # Появилась новая строка (phantom)

# В PostgreSQL:
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ)

4. SERIALIZABLE (сериализуемость)

# Самый строгий уровень — полная изоляция

# Защита от:
# ✅ Dirty Read
# ✅ Non-Repeatable Read
# ✅ Phantom Read

# Все операции выполняются так, как если бы они были одна за другой
# Но может быть медленнее из-за конфликтов

# Пример: две транзакции конкурируют
Тр1: BEGIN SERIALIZABLE
     SELECT balance FROM account WHERE id = 1  # $100
Тр2: BEGIN SERIALIZABLE
     UPDATE account SET balance = 50 WHERE id = 1
Тр2: COMMIT  # успешно
Тр1: UPDATE account SET balance = 80 WHERE id = 1
Тр1: COMMIT  # ОШИБКА: конфликт сериализации!

# В PostgreSQL:
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)

Таблица сравнения

Уровень              Dirty  Non-Rep  Phantom  Скорость
READ UNCOMMITTED      ❌     ❌       ❌       Очень быстро
READ COMMITTED        ✅     ❌       ❌       Быстро
REPEATABLE READ       ✅     ✅       ❌       Медленнее
SERIALIZABLE          ✅     ✅       ✅       Медленнее

Пример в SQLAlchemy

from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from sqlalchemy.orm.exc import StaleDataError

engine = create_engine(
    'postgresql://...',
    isolation_level='REPEATABLE READ'  # Уровень изоляции
)

with Session(engine) as session:
    # Все операции в этой сессии используют REPEATABLE READ
    user = session.query(User).filter(User.id == 1).first()
    user.balance -= 100
    session.commit()

Практический пример: обработка платежей

from sqlalchemy.orm import Session
from sqlalchemy import text

def transfer_money(
    session: Session,
    from_account_id: int,
    to_account_id: int,
    amount: float
):
    """
    Перевод денег между счетами
    Требует высокого уровня изоляции
    """
    try:
        # Начинаем с SERIALIZABLE для критичных операций
        session.execute(
            text("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE")
        )
        
        # Читаем баланс (в одной критичной операции)
        from_account = session.query(Account).filter(
            Account.id == from_account_id
        ).with_for_update().first()  # Блокируем строку
        
        to_account = session.query(Account).filter(
            Account.id == to_account_id
        ).with_for_update().first()
        
        # Проверяем и переводим
        if from_account.balance < amount:
            raise ValueError("Insufficient balance")
        
        from_account.balance -= amount
        to_account.balance += amount
        
        # Логируем
        session.add(Transaction(
            from_account_id=from_account_id,
            to_account_id=to_account_id,
            amount=amount
        ))
        
        session.commit()
        return True
    
    except StaleDataError:
        # Конфликт сериализации
        session.rollback()
        return False

Блокировки (Locking) — часть изоляции

from sqlalchemy import select

# FOR UPDATE — эксклюзивная блокировка
stmt = select(User).filter(User.id == 1).with_for_update()
user = session.execute(stmt).scalar_one()
# Никто другой не может изменять этого пользователя

# FOR SHARE — общая блокировка (только чтение)
stmt = select(User).filter(User.id == 1).with_for_update(read=True)
user = session.execute(stmt).scalar_one()
# Другие могут читать, но не могут писать

Обработка конфликтов

from sqlalchemy.exc import DBAPIError
from sqlalchemy.orm.exc import StaleDataError

def safe_transaction(session: Session):
    max_retries = 3
    for attempt in range(max_retries):
        try:
            # Критичная операция
            session.execute(
                text("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE")
            )
            
            # ... операции ...
            
            session.commit()
            return True
        
        except (StaleDataError, DBAPIError) as e:
            session.rollback()
            if attempt < max_retries - 1:
                print(f"Retry {attempt + 1}")
                continue
            else:
                raise

Оптимистичная блокировка (Optimistic Locking)

# Альтернатива пессимистичной блокировке
# Добавляем поле версии

from sqlalchemy import Column, Integer

class Product(Base):
    __tablename__ = 'products'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    price = Column(Float)
    version = Column(Integer, default=0)  # Версия

# При обновлении:
def update_price(session: Session, product_id: int, new_price: float):
    product = session.query(Product).filter(
        Product.id == product_id
    ).first()
    
    old_version = product.version
    product.price = new_price
    product.version = old_version + 1
    
    # Проверяем, что версия не изменилась
    result = session.execute(
        text("""
        UPDATE products 
        SET price = :price, version = :new_version
        WHERE id = :id AND version = :old_version
        """),
        {
            'price': new_price,
            'new_version': old_version + 1,
            'id': product_id,
            'old_version': old_version
        }
    )
    
    if result.rowcount == 0:
        raise ValueError("Version conflict - concurrent update detected")

Выбор уровня изоляции

Используй READ COMMITTED, если:

  • Большинство операций читают данные
  • Некритичны малые несогласованности
  • Нужна максимальная производительность

Используй REPEATABLE READ, если:

  • Операции работают с одними и теми же данными
  • Нужна хорошая производительность + безопасность
  • Фонаво в production

Используй SERIALIZABLE, если:

  • Операции критичны (платежи, транзакции)
  • Нужна полная гарантия консистентности
  • Готов пожертвовать производительностью

Вывод

Изоляция обеспечивает:

  • Независимость одновременных транзакций
  • Консистентность данных
  • Предсказуемое поведение при параллелизме
  • Защиту от потери данных

Выбор уровня изоляции — это баланс между безопасностью и производительностью.

Что такое изоляция? | PrepBro