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

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

1.2 Junior🔥 291 комментариев
#Тестирование

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

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

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

Изоляция в ACID

Изоляция (Isolation) — это четвёртый принцип ACID (Atomicity, Consistency, Isolation, Durability), который гарантирует, что одновременно выполняемые транзакции не влияют друг на друга и не видят незавершённые изменения друг друга.

Что такое ACID

ACID — это набор свойств, которые гарантируют надёжность транзакций в базе данных:

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

Проблема конкурентности без изоляции

Без правильной изоляции возникают следующие проблемы:

Dirty Read (грязное чтение):

Транзакция 1              | Транзакция 2
---------------------------------
БЕГИН
                           | НАЧАЛО
УПДАЙТ balance = 100
                           | SELECT balance
                           | Видит 100 (незавершённое изменение!)
ROLLBACK                    |
(откат на 50)              | COMMIT

Транзакция 2 прочитала значение, которое было откачено

Non-repeatable Read (неповторяющееся чтение):

Транзакция 1              | Транзакция 2
---------------------------------
БЕГИН
SELECT balance = 100
                           | UPDATE balance = 200
                           | COMMIT
SELECT balance = 200

Одна транзакция видит разные значения при двух чтениях

Phantom Read (фантомное чтение):

Транзакция 1              | Транзакция 2
---------------------------------
BEGIN
SELECT * FROM users
WHERE age > 20
(результат: 5 пользователей)
                           | INSERT INTO users VALUES (...)
                           | COMMIT
SELECT * FROM users
WHERE age > 20
(результат: 6 пользователей!)

Добавились новые строки, которые соответствуют условию

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

БД предлагают различные уровни изоляции для баланса между безопасностью и производительностью:

1. READ UNCOMMITTED (Чтение незавершённых данных)

# Наименьший уровень изоляции
# Позволяет dirty reads
# Практически не используется

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import Session

engine = create_engine(
    "postgresql://user:password@localhost/db",
    isolation_level="READ UNCOMMITTED"
)

# Транзакция может видеть незавершённые изменения других транзакций

2. READ COMMITTED (Чтение завершённых данных)

# Умолчальный уровень в PostgreSQL и Oracle
# Предотвращает dirty reads
# Позволяет non-repeatable reads и phantom reads

engine = create_engine(
    "postgresql://user:password@localhost/db",
    isolation_level="READ COMMITTED"
)

# Транзакция видит только завершённые изменения
with Session(engine) as session:
    user = session.query(User).filter(User.id == 1).first()
    # Если другая транзакция обновила user — получим новое значение

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

# Предотвращает dirty reads и non-repeatable reads
# Позволяет phantom reads

engine = create_engine(
    "postgresql://user:password@localhost/db",
    isolation_level="REPEATABLE READ"
)

with Session(engine) as session:
    users = session.query(User).filter(User.age > 20).all()
    # Если другая транзакция добавит пользователя — мы его не увидим
    # (но новые строки в другом запросе всё ещё могут появиться)

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

# Наивысший уровень изоляции
# Предотвращает все проблемы: dirty reads, non-repeatable reads, phantom reads
# Самый медленный, так как транзакции выполняются практически последовательно

engine = create_engine(
    "postgresql://user:password@localhost/db",
    isolation_level="SERIALIZABLE"
)

with Session(engine) as session:
    # Гарантирует, что транзакция видит данные такими, как если бы
    # все другие транзакции выполнялись либо до, либо после неё
    balance = session.query(Account).filter(Account.id == 1).first()
    balance.amount -= 100
    session.commit()

Таблица уровней изоляции

Уровень изоляции    | Dirty Read | Non-rep. Read | Phantom Read
-----------------------------------------------------------------
READ UNCOMMITTED    | Да        | Да           | Да
READ COMMITTED      | Нет       | Да           | Да
REPEATABLE READ     | Нет       | Нет          | Да
SERIALIZABLE        | Нет       | Нет          | Нет

Реализация в PostgreSQL

from sqlalchemy import create_engine, text
from sqlalchemy.orm import Session

engine = create_engine("postgresql://user:password@localhost/db")

# Установка уровня изоляции
with engine.connect() as connection:
    # Для текущей сессии
    connection.execute(text(
        "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"
    ))
    
    # Выполнение операции
    result = connection.execute(text("SELECT * FROM users"))
    data = result.fetchall()
    
    connection.commit()

Пример с использованием SQLAlchemy

from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.exc import SQLAlchemyError

SessionLocal = sessionmaker(bind=engine)

def transfer_money(from_account_id: int, to_account_id: int, amount: float):
    """Перевод денег между счётами с изоляцией SERIALIZABLE"""
    session = SessionLocal()
    
    try:
        # Устанавливаем уровень изоляции
        session.connection().connection.isolation_level = "SERIALIZABLE"
        
        # Получаем счёты
        from_account = session.query(Account).filter(
            Account.id == from_account_id
        ).with_for_update().first()  # FOR UPDATE блокирует строку
        
        to_account = session.query(Account).filter(
            Account.id == to_account_id
        ).with_for_update().first()
        
        # Проверяем баланс
        if from_account.balance < amount:
            raise ValueError("Недостаточно средств")
        
        # Выполняем операцию
        from_account.balance -= amount
        to_account.balance += amount
        
        session.commit()
        return True
    
    except SQLAlchemyError as e:
        session.rollback()
        raise
    finally:
        session.close()

Блокировки для обеспечения изоляции

Пессимистическая блокировка (FOR UPDATE):

# Блокирует строку для других транзакций
from sqlalchemy import select

user = session.query(User).filter(
    User.id == 1
).with_for_update().first()  # Блокировка на чтение

user.balance += 100
session.commit()

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

from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    version = Column(Integer, default=1)  # Версия для оптимистичной блокировки
    
    def update_name(self, new_name: str, session):
        # Проверяем версию перед обновлением
        current = session.query(User).filter(User.id == self.id).first()
        if current.version != self.version:
            raise ValueError("Data was modified")
        
        self.name = new_name
        self.version += 1
        session.commit()

Best practices

  • Выбирай правильный уровень изоляции — SERIALIZABLE медленный, но безопасный
  • Используй блокировки FOR UPDATE — для критичных операций (перевод денег)
  • Минимизируй время транзакции — чем дольше транзакция, тем больше конфликтов
  • Тестируй race conditions — используй concurrent тесты для проверки
  • Документируй требования — какой уровень изоляции нужен для каждой операции

Вывод: Изоляция — это ключевое свойство для надёжности данных при одновременной работе. Выбор правильного уровня изоляции критичен для баланса между безопасностью и производительностью.

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