django-4事务

发布于:2025-07-31 ⋅ 阅读:(13) ⋅ 点赞:(0)

Django 事务(Transaction)是保证数据库操作原子性的关键机制,用于确保一组数据库操作要么全部成功提交,要么在发生错误时全部回滚,从而维持数据的一致性。

事务的 ACID 特性

Django 事务遵循数据库事务的四大特性:

  • 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不执行。
  • 一致性(Consistency):事务执行前后,数据库从一个有效状态转换到另一个有效状态(如约束、关联关系不变)。
  • 隔离性(Isolation):多个事务并发执行时,彼此不会相互干扰(隔离级别决定干扰程度)。
  • 持久性(Durability):事务提交后,修改会永久保存到数据库,即使系统崩溃也不会丢失。

事务的使用方式

装饰器 @transaction.atomic(最常用)

将整个函数包装在事务中,函数内所有数据库操作作为一个原子单元。

from django.db import transaction
from myapp.models import Book, Order

@transaction.atomic
def create_order(book_id, user_id, quantity):
    """创建订单并扣减库存(原子操作)"""
    # 1. 查询书籍(加行锁防止并发问题)
    book = Book.objects.select_for_update().get(id=book_id)
    
    # 2. 检查库存
    if book.stock < quantity:
        raise ValueError("库存不足")  # 触发回滚
    
    # 3. 创建订单
    order = Order.objects.create(
        user_id=user_id,
        book=book,
        quantity=quantity,
        total_price=book.price * quantity
    )
    
    # 4. 扣减库存
    book.stock -= quantity
    book.save()
    
    return order
  • 若函数正常执行,事务自动提交。
  • 若抛出未捕获的异常(如 ValueError),事务自动回滚。

上下文管理器 with transaction.atomic()

在代码块级别使用事务,更灵活地控制事务范围。

  • 适合仅需对部分代码进行事务控制的场景。
  • 代码块执行完毕自动提交,异常时自动回滚。
def update_inventory(book_id, new_stock):
    try:
        # 事务代码块
        with transaction.atomic():
            book = Book.objects.get(id=book_id)
            book.stock = new_stock
            book.save()
            # 其他操作...
        print("操作成功,已提交")
    except Exception as e:
        print(f"操作失败,已回滚: {e}")

手动控制事务(低级 API)

通过 transaction.begin()transaction.commit()transaction.rollback() 手动管理事务(不推荐,容易出错)。

def manual_transaction():
    # 开始事务
    transaction.set_autocommit(False)  # 关闭自动提交
    try:
        book = Book.objects.get(id=1)
        book.stock -= 1
        book.save()
        # 手动提交
        transaction.commit()
    except:
        # 出错时回滚
        transaction.rollback()
    finally:
        # 恢复自动提交模式
        transaction.set_autocommit(True)

事务隔离级别

数据库事务的隔离级别决定了并发事务之间的可见性,Django 支持通过设置隔离级别控制这一行为。
可选隔离级别(因数据库而异)

  • READ UNCOMMITTED:最低隔离级别,允许读取未提交的数据(脏读)。
  • READ COMMITTED:默认级别(多数数据库),只能读取已提交的数据(避免脏读)。
  • REPEATABLE READ:确保同一事务中多次读取的数据一致(避免不可重复读)。
  • SERIALIZABLE:最高隔离级别,事务串行执行(避免幻读,性能最低)。

在 Django 中设置隔离级别

全局设置 settings.py)

DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.postgresql',
       'NAME': 'mydb',
       'OPTIONS': {
           'isolation_level': 'read committed',  # 或其他级别
       },
   }
}

局部设置(针对特定事务):

from django.db import transaction

@transaction.atomic(isolation_level='serializable')
def critical_operation():
    # 高隔离级别的关键操作
    pass

事务保存点(Savepoint)

在复杂事务中,可设置保存点,允许回滚到事务中的特定位置,而不是整个事务。

  • transaction.savepoint():创建保存点,返回标识。
  • transaction.savepoint_rollback(savepoint):回滚到指定保存点。
  • transaction.savepoint_commit(savepoint):提交保存点(使其成为事务的一部分)。
@transaction.atomic
def complex_operation():
    # 操作1:创建用户
    user = User.objects.create(username="test")
    
    # 设置保存点
    savepoint = transaction.savepoint()
    
    try:
        # 操作2:创建订单(可能失败)
        order = Order.objects.create(user=user, total=100)
    except Exception as e:
        # 回滚到保存点(仅撤销操作2,保留操作1)
        transaction.savepoint_rollback(savepoint)
        print(f"订单创建失败,已回滚到保存点: {e}")
    else:
        # 提交保存点(操作2生效)
        transaction.savepoint_commit(savepoint)
    
    # 操作3:无论操作2是否成功,都会执行
    user.is_active = True
    user.save()

并发控制与锁

事务中常需处理并发问题,Django 提供了两种锁机制:

行级锁 select_for_update()

查询时对记录加行锁,阻止其他事务修改该记录,直到当前事务完成。

  • 适用于并发修改同一记录的场景(如秒杀、库存扣减)。
  • 仅在事务中有效(需配合 atomic)。
@transaction.atomic
def decrease_stock(book_id, quantity):
    # 加行锁查询(其他事务需等待锁释放)
    book = Book.objects.select_for_update().get(id=book_id)
    if book.stock >= quantity:
        book.stock -= quantity
        book.save()
    else:
        raise ValueError("库存不足")

表级锁(不推荐)

通过 select_for_update(of=…) 或原生 SQL 实现,会锁定整张表,性能较差,谨慎使用。

注意事项

事务范围不宜过大

避免在事务中包含耗时操作(如网络请求、大量计算),否则会长期占用数据库连接,降低并发性能。

@transaction.atomic
def bad_example():
    book = Book.objects.get(id=1)
    send_email()  # 耗时的网络操作,不应放在事务中
    book.stock -= 1
    book.save()

异常处理

事务仅对数据库操作生效,非数据库操作(如文件写入)不会随事务回滚,需手动处理。

@transaction.atomic
def handle_file_and_db():
    file_path = "data.txt"
    try:
        # 数据库操作
        book = Book.objects.get(id=1)
        book.stock += 1
        book.save()
        
        # 文件操作
        with open(file_path, "w") as f:
            f.write("data")
    except:
        # 手动删除已创建的文件
        if os.path.exists(file_path):
            os.remove(file_path)
        raise  # 触发数据库回滚

自动提交模式

Django 默认开启自动提交(autocommit=True),即每个数据库操作单独成事务。使用 atomic 时会临时关闭自动提交,结束后恢复。


网站公告

今日签到

点亮在社区的每一天
去签到