Что такое атомарная транзакция?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Атомарная транзакция в базах данных
Атомарная транзакция — это последовательность операций в базе данных, которые выполняются как одно неделимое целое: либо все выполнены успешно, либо ни одна. Это первый принцип ACID.
Концепция атомарности
Без атомарности (опасно):
├─ Деньги списаны со счёта Алисы ✓
├─ ОШИБКА: система падает
└─ Деньги НЕ поступили Бобу ✗
РЕЗУЛЬТАТ: деньги потеряны!
С атомарностью (безопасно):
├─ Деньги списаны со счёта Алисы
├─ Деньги поступили Бобу
├─ ОШИБКА: система падает
└─ Всё откатывается к начальному состоянию
РЕЗУЛЬТАТ: либо всё выполнено, либо ничего
Пример реального вреда
Сценарий: перевод денег между счётами
Счёт Алисы: 1000 руб
Счёт Боба: 500 руб
Алиса переводит 200 руб Бобу
БЕЗ атомарности (плохо)
def transfer_money(alice_id, bob_id, amount):
# Шаг 1: списать со счёта Алисы
alice = Account.objects.get(id=alice_id)
alice.balance -= amount
alice.save() # Сохранено!
# ОШИБКА здесь! Система падает
raise Exception("Сетевая ошибка")
# Шаг 2: добавить на счёт Боба (НИКОГДА не выполнится)
bob = Account.objects.get(id=bob_id)
bob.balance += amount
bob.save()
РЕЗУЛЬТАТ:
Счёт Алисы: 800 руб (деньги списаны!)
Счёт Боба: 500 руб (деньги НЕ пришли!)
Деньги потеряны!
С атомарностью (хорошо)
from django.db import transaction
@transaction.atomic
def transfer_money(alice_id, bob_id, amount):
# Шаг 1: списать со счёта Алисы
alice = Account.objects.get(id=alice_id)
alice.balance -= amount
alice.save()
# ОШИБКА здесь!
raise Exception("Сетевая ошибка")
# Шаг 2: добавить на счёт Боба
bob = Account.objects.get(id=bob_id)
bob.balance += amount
bob.save()
РЕЗУЛЬТАТ:
Счёт Алисы: 1000 руб (ОТКАЧЕНО!)
Счёт Боба: 500 руб (ОТКАЧЕНО!)
Всё остаётся как было - безопасно!
ACID в деталях
A — Atomicity (Атомарность)
- Либо все операции выполнены, либо ни одна
- Нет промежуточных состояний
C — Consistency (Согласованность)
- База остаётся в согласованном состоянии
- Все ограничения (constraints) соблюдены
I — Isolation (Изоляция)
- Одновременные транзакции не мешают друг другу
- Каждая видит согласованное состояние
D — Durability (Надёжность)
- Успешно выполненная транзакция сохранится
- Даже при сбое системы
Использование транзакций в Django
Декоратор @transaction.atomic
from django.db import transaction
@transaction.atomic
def create_order(user, products):
# Создать заказ
order = Order.objects.create(user=user)
# Добавить товары
for product in products:
OrderItem.objects.create(
order=order,
product=product,
quantity=product.quantity
)
# Обновить количество на складе
for product in products:
product.stock -= product.quantity
product.save()
# Если всё выполнено — автоматически commit
# Если ошибка — автоматически rollback
return order
Context manager (with statement)
from django.db import transaction
def process_payment(order_id):
with transaction.atomic():
order = Order.objects.get(id=order_id)
# Списать деньги
payment = Payment.objects.create(
order=order,
amount=order.total_price,
status='pending'
)
# Проверить баланс (может выбросить исключение)
check_balance(order.user)
# Отправить на процессинг
payment.process()
# Обновить статус заказа
order.status = 'paid'
order.save()
# Всё успешно — commit
# Если исключение — rollback и re-raise
Commit и Rollback
Commit — сохранить изменения
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("UPDATE accounts SET balance = balance - 200 WHERE id = 1")
cursor.execute("UPDATE accounts SET balance = balance + 200 WHERE id = 2")
# Автоматический commit в конце блока
Rollback — отменить изменения
from django.db import transaction
try:
with transaction.atomic():
# Операции
update_inventory()
process_payment() # Может выбросить исключение
except PaymentFailed:
# Автоматический rollback — inventory вернётся в прежнее состояние
print("Платёж не пошёл, инвентарь откачен")
Уровни изоляции (Isolation Levels)
Разные БД поддерживают разные уровни:
Read Uncommitted — самый слабый
Транзакция 1: UPDATE balance = 500
Транзакция 2: READ balance = 500 (грязное чтение!)
Транзакция 1: ROLLBACK
Транзакция 2: видела 500, но было отачено!
Read Committed — стандартно
Транзакция 1: UPDATE balance = 500
Транзакция 2: READ balance = 300 (не видит незафиксированные)
Транзакция 1: COMMIT
Транзакция 2: READ balance = 500 (теперь видит)
Repeatable Read — лучше
Транзакция 1: READ balance = 300
Транзакция 2: UPDATE balance = 500, COMMIT
Транзакция 1: READ balance = 300 (одно и то же)
Serializable — самый строгий
База выполняет транзакции одну за другой
Медленнее, но максимально безопасно
В Django
from django.db import transaction
with transaction.atomic():
# READ_COMMITTED (по умолчанию)
# Может быть разное поведение в разных БД
pass
Practical Pitfalls (частые ошибки)
Ошибка 1: N+1 queries внутри транзакции
# ПЛОХО
@transaction.atomic
def bad_order_processing():
for product in products: # Цикл
product.refresh_from_db() # N запросов!
# ХОРОШО
@transaction.atomic
def good_order_processing():
products = Product.objects.all() # 1 запрос
for product in products:
process(product) # Уже в памяти
Ошибка 2: Deadlock (взаимная блокировка)
# Транзакция 1: блокирует A, ждёт B
# Транзакция 2: блокирует B, ждёт A
# → DEADLOCK!
# Решение: всегда блокируй в одинаковом порядке
with transaction.atomic():
a = A.objects.select_for_update().get(id=1)
b = B.objects.select_for_update().get(id=1)
Ошибка 3: Слишком длинная транзакция
# ПЛОХО
@transaction.atomic
def slow_import():
for row in large_csv:
# Долгая операция
process_row(row)
# Транзакция открыта весь час!
# ХОРОШО
def fast_import():
for row in large_csv:
with transaction.atomic():
process_row(row) # Каждая строка — отдельная транзакция
Raw SQL с транзакциями
from django.db import connection, transaction
def transfer_with_raw_sql(alice_id, bob_id, amount):
with transaction.atomic():
with connection.cursor() as cursor:
cursor.execute(
"UPDATE accounts SET balance = balance - %s WHERE id = %s",
[amount, alice_id]
)
cursor.execute(
"UPDATE accounts SET balance = balance + %s WHERE id = %s",
[amount, bob_id]
)
# Commit при выходе из блока
Резюме
Атомарная транзакция — это гарантия целостности данных. Без неё денег можно терять, товары дублировать, и счёты не сходиться. Каждый Python разработчик должен понимать, как правильно использовать транзакции в своём коде.