Какие знаешь свойства транзакций?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
ACID свойства транзакций в базах данных
Транзакция — это последовательность операций над базой данных, которая должна выполняться либо полностью, либо не выполняться вообще. Надёжность транзакций обеспечивают четыре принципа ACID.
1. Atomicity (Атомарность)
Транзакция либо полностью выполняется, либо полностью откатывается. Нет промежуточных состояний.
Пример — перевод денег между счётами:
# BEGIN TRANSACTION
# UPDATE accounts SET balance = balance - 100 WHERE id = 1;
# UPDATE accounts SET balance = balance + 100 WHERE id = 2;
# COMMIT;
Если во время второго UPDATE произойдёт сбой:
- Или обе операции выполнены (деньги переведены)
- Или обе откатаны (деньги не переведены)
- Никогда не будет ситуации, когда у одного счёта минус 100, а у другого плюс 0
Реализация в Python (SQLAlchemy):
from sqlalchemy.orm import Session
try:
session = Session(engine)
# Все операции в одной транзакции
account1 = session.query(Account).filter_by(id=1).with_for_update().one()
account2 = session.query(Account).filter_by(id=2).with_for_update().one()
account1.balance -= 100
account2.balance += 100
session.commit() # Атомарно
except Exception as e:
session.rollback() # Откат
raise
2. Consistency (Согласованность)
База данных переходит из одного согласованного состояния в другое. Все ограничения целостности (constraints) соблюдаются до и после транзакции.
Примеры ограничений:
- PRIMARY KEY — уникальность ID
- FOREIGN KEY — ссылки между таблицами корректны
- CHECK — проверка условий (age > 0)
- NOT NULL — обязательные поля
- UNIQUE — уникальность значения
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
age INTEGER CHECK (age >= 18),
department_id INTEGER REFERENCES departments(id)
);
-- Попытка вставить некорректные данные
INSERT INTO users (id, email, age)
VALUES (1, 'user@example.com', 15); -- ОШИБКА: age < 18
INSERT INTO users (id, email, age)
VALUES (1, NULL, 25); -- ОШИБКА: email NULL
INSERT INTO users (id, email, age)
VALUES (1, 'test@example.com', 25); -- OK
В Python:
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
email = Column(String(255), unique=True, nullable=False)
age = Column(Integer, CheckConstraint('age >= 18'))
department_id = Column(Integer, ForeignKey('departments.id'))
# При сохранении БД проверит все constraints
session.add(User(id=1, email='user@example.com', age=25))
session.commit() # Гарантирует согласованность
3. Isolation (Изолированность)
Одновременные транзакции не должны мешать друг другу. Каждая видит согласованный снимок данных.
Проблемы без изоляции:
Dirty Read (грязное чтение)
Транзакция B видит незафиксированные изменения от транзакции A.
Трансакция A Транзакция B
BEGIN
UPDATE balance = 100
SELECT balance <- видит 100 (ошибка!)
ROLLBACK
(баланс остался 50)
Non-repeatable Read (нестабильное чтение)
Два чтения одного значения в одной транзакции дают разные результаты.
Трансакция A Транзакция B
BEGIN
SELECT age = 25
UPDATE age = 30
COMMIT
SELECT age = 30 (было 25!) <- противоречие
COMMIT
Phantom Read (фантомное чтение)
Два чтения одного набора строк дают разные результаты (добавились или удалились строки).
Трансакция A Транзакция B
BEGIN
SELECT COUNT(*) = 5
INSERT new_row
COMMIT
SELECT COUNT(*) = 6 (было 5!) <- фантом
COMMIT
Уровни изоляции (от низкого к высокому):
# SERIALIZABLE (самый строгий)
# Как будто транзакции выполняются последовательно
session.execute(text('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'))
# REPEATABLE READ (PostgreSQL по умолчанию)
# Защита от Dirty Read и Non-repeatable Read
# Но возможны Phantom Read
# READ COMMITTED (MySQL по умолчанию)
# Защита только от Dirty Read
# Возможны Non-repeatable Read и Phantom Read
# READ UNCOMMITTED (небезопасно)
# Без защиты от любых проблем
# В SQLAlchemy:
from sqlalchemy import text
session.execute(text('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'))
4. Durability (Долговечность)
Однажды подтверждённые данные сохраняются навсегда, даже при сбое системы.
Как это работает:
- Все операции перед COMMIT логируются на диск (Write-Ahead Log)
- Только после успешной записи в лог выполняется COMMIT
- При восстановлении после сбоя можно восстановить все подтверждённые транзакции
Процесс с Durability:
┌─────────────────┐
│ Application │ Отправляет COMMIT
└────────┬────────┘
│
┌────────▼────────┐
│ Write-Ahead Log │ Пишет в лог на диск
└────────┬────────┘
│
┌────────▼────────┐
│ Main Database │ Применяет изменения
└────────┬────────┘
│
┌────────▼────────┐
│ COMMITTED ✓ │ Подтверждено
└─────────────────┘
Если сбой между Write-Ahead Log и Main Database,
при восстановлении система применит изменения из лога.
Если сбой после COMMITTED, данные уже на диске.
Пример транзакции с ACID
from sqlalchemy.orm import Session
from sqlalchemy import create_engine, text
engine = create_engine('postgresql://user:password@localhost/db')
def transfer_money(from_user_id: int, to_user_id: int, amount: float):
"""Безопасный перевод денег между счётами (ACID гарантирует)"""
with Session(engine) as session:
try:
# ATOMICITY: либо обе операции, либо ничего
from_account = session.query(Account).filter_by(id=from_user_id).with_for_update().one()
to_account = session.query(Account).filter_by(id=to_user_id).with_for_update().one()
# CONSISTENCY: проверка constraints
if from_account.balance < amount:
raise ValueError("Insufficient balance")
# Выполняем операции
from_account.balance -= amount
to_account.balance += amount
# Создаём транзакцию в audit log (CONSISTENCY)
transaction = Transaction(
from_user_id=from_user_id,
to_user_id=to_user_id,
amount=amount,
status="completed"
)
session.add(transaction)
# ISOLATION: другие транзакции не видят промежуточные состояния
# DURABILITY: после commit данные безопасно на диске
session.commit()
return True
except Exception as e:
session.rollback()
print(f"Transaction failed: {e}")
return False
# Использование
transfer_money(from_user_id=1, to_user_id=2, amount=100)
Итог
| Свойство | Гарантирует |
|---|---|
| Atomicity | Либо всё, либо ничего |
| Consistency | Соблюдение всех ограничений целостности |
| Isolation | Независимость одновременных транзакций |
| Durability | Постоянность подтверждённых данных |
ACID-транзакции критичны для любого приложения, где нужна надёжность: финансовые системы, системы управления заказами, учёт инвентаря.