З транзакціями в реальному житті ви стикалися неодноразово. Припустимо, ви збираєтеся щось купити і оплатити покупку банківською карткою. Нехай це буде морозиво. Ви прикладаєте картку до терміналу, тим самим відправляючи запит у банк, банк перевіряє баланс, списує потрібну суму, і тільки потім угода вважається завершеною. Якщо якийсь із цих кроків не виконаний, ваші гроші залишаться на рахунку, і ви не отримаєте морозиво. Кожен такий крок — це частина операції, а вся послідовність кроків — це транзакція.
Транзакція — це група операцій, які виконуються як єдине ціле. Або всі операції транзакції завершуются успішно (commit), або у випадку помилки вони відкатуються (rollback), залишаючи систему в початковому стані. Це допомагає забезпечити узгодженість і цілісність даних.
ACID: основи консистентності даних
Термін ACID описує ключові властивості транзакцій:
- Atomicity (Атомарність) — транзакція або виконується повністю, або не виконується зовсім.
- Consistency (Сумісність / Узгодженість) — після завершення транзакції система залишається в узгодженому стані.
- Isolation (Ізоляція) — паралельні транзакції не конфліктують одна з одною.
- Durability (Надійність) — результати успішної транзакції зберігаються навіть у випадку збою системи.
Без транзакцій робота з даними стає хаотичною. Наприклад:
- Якщо ви виконуєте кілька пов'язаних операцій (наприклад, оновлюєте баланс клієнта і записуєте транзакцію в журнал), збій в одній з них призведе до неузгодженості даних.
- При паралельній обробці запитів може виникнути гонка даних (data race), коли кілька користувачів одночасно оновлюють один і той же запис.
Транзакції рятують нас від таких проблем, перетворюючи базу даних на спокійне місце.
Особливості транзакцій в SQLAlchemy
SQLAlchemy надає зручний механізм для роботи з транзакціями. Ключові дії:
- Початок транзакції. Операція починається автоматично, коли ви виконуєте першу команду (наприклад, додавання або зміну даних).
- Фіксація змін (commit). Якщо всі операції успішні, зміни фіксуються в базі даних.
- Відкат змін (rollback). Якщо щось пішло не так, всі зміни відкотяться, база повернеться до початкового стану.
Основні підходи
SQLAlchemy підтримує два підходи для роботи з транзакціями:
- Ручне керування транзакціями. Ви вручну починаєте транзакцію і використовуєте
commitабоrollback. - Контекстний менеджер. Більш лаконічний спосіб, який автоматично обробляє помилки і керує транзакцією.
Приклади використання транзакцій
Давайте попрацюємо з кодом, тому що світ транзакцій найкраще розкривається на практиці. Розглянемо кілька цікавих прикладів.
Приклад: ручне керування транзакцією
Ось приклад явного керування транзакцією:
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
# Створюємо базу даних SQLite
engine = create_engine('sqlite:///example.db')
Base = declarative_base()
# Визначаємо модель
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
# Створюємо таблиці
Base.metadata.create_all(engine)
# Створюємо сесію
Session = sessionmaker(bind=engine)
session = Session()
# Починаємо транзакцію
try:
new_user = User(name="Alice")
session.add(new_user)
session.commit() # Фіксуємо транзакцію
print("Користувача збережено!")
except Exception as e:
session.rollback() # Відкат транзакції у разі помилки
print(f"Сталася помилка: {e}")
finally:
session.close()
У цьому прикладі:
- Ми додаємо нового користувача.
- Якщо виникає помилка при додаванні, зміни відкатуються.
Приклад: використання контекстного менеджера
SQLAlchemy значно спрощує роботу з транзакціями через контекстний менеджер:
with Session() as session:
try:
new_user = User(name="Bob")
session.add(new_user)
session.commit()
print("Користувача збережено!")
except Exception as e:
session.rollback()
print(f"Сталася помилка: {e}")
Цей код робить те саме, але виглядає чистіше. Контекстний менеджер автоматично закриває сесію, а ви керуєте тільки фіксацією або відкатом.
Як застосовувати транзакції для виконання кількох операцій
Транзакції корисні, якщо вам потрібно виконати кілька пов'язаних операцій. Наприклад, якщо ми створюємо замовлення в інтернет-магазині:
- Зменшити кількість товару на складі.
- Створити запис про замовлення.
- Списати гроші з балансу користувача.
Ці операції залежать одна від одної. Якщо одна з них не вдалася, система повинна повернутися до початкового стану.
Приклад з кількома операціями
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, declarative_base, relationship
# Налаштовуємо базу даних
engine = create_engine('sqlite:///shop.db')
Base = declarative_base()
Session = sessionmaker(bind=engine)
# Моделі
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String)
stock = Column(Integer)
class Order(Base):
__tablename__ = 'orders'
id = Column(Integer, primary_key=True)
product_id = Column(Integer, ForeignKey('products.id'))
quantity = Column(Integer)
# Створюємо таблиці
Base.metadata.create_all(engine)
# Приклад транзакції
with Session() as session:
try:
# Зменшуємо залишки на складі
product = session.query(Product).filter_by(name="Laptop").first()
if product.stock < 1:
raise ValueError("Товару немає в наявності!")
product.stock -= 1
# Створюємо замовлення
order = Order(product_id=product.id, quantity=1)
session.add(order)
# Фіксуємо зміни
session.commit()
print("Замовлення успішно створено!")
except Exception as e:
# Відкат у разі помилки
session.rollback()
print(f"Не вдалося створити замовлення: {e}")
Тут ми:
- Перевіряємо доступність товару на складі.
- Зменшуємо кількість.
- Створюємо замовлення.
- У разі помилки відкатуємо всі зміни.
Підводні камені та типові помилки
Робота з транзакціями може бути складною, якщо не враховувати деталі:
- Багатопоточність. Сесії SQLAlchemy не потокобезпечні, тому для кожного потоку/запиту створюйте окрему сесію.
- Повторне використання сесії. Не намагайтеся повторно використовувати ту саму сесію після помилки.
- Збій після commit. Якщо помилка сталася після фіксації транзакції, відкотити зміни вже неможливо.
Практичне застосування
Транзакції — це не просто теорія, вони відіграють життєво важливу роль у реальних проєктах:
- Інтернет-магазини. Управління замовленнями, балансами і списання товарів.
- Фінансові додатки. Перекази, управління балансом і обробка платіжних операцій.
- CRM-системи. Одночасне оновлення даних і пов'язаних сутностей.
Коли ви опануєте роботу з транзакціями, ви зможете проєктувати системи, стійкі до збоїв і помилок, що зробить ваш застосунок справжнім шедевром інженерної думки.
Докладніше про транзакції в SQLAlchemy можна прочитати в документації.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ