Django MySQL 事务机制与回滚行为说明

  • Django 中 transaction.atomic() 的事务行为进行系统性总结,重点说明:哪些异常会触发自动回滚、哪些情况不会、为何产生差异,以及如何正确编写可控的事务逻辑。

1. transaction.atomic() 的基本行为

transaction.atomic() 会在进入时创建一个 数据库保存点(savepoint)

代码块退出时根据执行结果决定回滚或提交:

  • 若出现 未捕获的异常:自动回滚到保存点,并继续抛出该异常。
  • 若代码执行正常结束:提交保存点。

核心原则:是否回滚取决于是否有异常向外抛出。


2. 会触发自动回滚的情况

2.1 未捕获的 Python 异常

只要异常没有在 atomic 块内被捕获,都会触发回滚,包括:

  • ZeroDivisionError
  • ValueError
  • TypeError
  • KeyError
  • IndexError
  • AssertionError
  • 自定义异常
  • raise Exception("...")

示例:

python 复制代码
with transaction.atomic():
    obj = Model.objects.create(...)
    raise Exception("abort transaction")  # 未捕获 → 自动回滚

2.2 未捕获的 Django ORM / 数据库异常

数据库相关异常未被捕获时,也会触发自动回滚,包括:

  • IntegrityError(唯一约束、外键约束)
  • DataError(无效数据)
  • OperationalError(死锁、锁超时)
  • ProgrammingError(SQL 错误)
  • DatabaseError
  • InterfaceError

示例:

python 复制代码
with transaction.atomic():
    Model.objects.create(id=1)
    Model.objects.create(id=1)  # IntegrityError → 自动回滚

2.3 DRF 验证错误(raise_exception=True)

python 复制代码
serializer.is_valid(raise_exception=True)

该语句抛出的 ValidationError 未被捕获 → 自动回滚。


3. 不会自动回滚的情况

3.1 异常被捕获并吞掉

只要异常被捕获,没有继续向外抛出,Django 会视为"执行成功",不会回滚。

python 复制代码
with transaction.atomic():
    try:
        raise Exception("error")
    except Exception:
        pass  # 异常被吞掉 → 不回滚

若需要强制回滚,必须手动调用:

python 复制代码
transaction.set_rollback(True)

3.2 没有异常但提前 return

业务判断不属于错误,不会自动回滚。

python 复制代码
with transaction.atomic():
    if invalid:
        return Response({"error": ...})  # 不回滚

3.3 DRF 验证错误未 raise

python 复制代码
serializer.is_valid(raise_exception=False)
if serializer.errors:
    return Response(serializer.errors)  # 不回滚

只有抛出异常才会回滚。


3.4 数据库层无异常但逻辑错误

例如外键检查关闭、不符合约束但 MySQL 未抛错,此类不会产生自动回滚。


3.5 使用 atomic(savepoint=False) 并在内部捕获异常

python 复制代码
with transaction.atomic(savepoint=False):
    try:
        raise Exception()
    except:
        pass

内部异常被捕获 → 无异常向上抛出 → 不会回滚。


4. raise Exception('xxx') 是否回滚?

答案:会回滚,只要未被捕获。

raise Exception('xxx') 会触发 Python 异常,atomic 在退出时检测到异常 → 自动回滚保存点。

示例:

python 复制代码
with transaction.atomic():
    raise Exception("abort")  # 自动回滚

若被捕获,则不会回滚:

python 复制代码
with transaction.atomic():
    try:
        raise Exception("abort")
    except Exception:
        pass  # 无异常向上抛出 → 不回滚

5. 总结:事务回滚的唯一决定因素

Django 中事务是否回滚,唯一决定因素:

是否有异常从 atomic 块中冒出。

可以总结为:

  • 未捕获异常 → 自动回滚
  • 捕获异常 → 不自动回滚
  • 没有异常 → 正常提交
  • DRF raise_exception=True → 会回滚
  • return 并非错误 → 不回滚

6. 推荐的事务编写模式(最佳实践)

python 复制代码
from django.db import transaction

def perform_action():
    try:
        with transaction.atomic():
            # 业务逻辑
            do_something()

    except Exception as e:
        # atomic 已自动回滚
        handle_error(e)
        raise

如果希望在捕获异常后仍然回滚:

python 复制代码
with transaction.atomic():
    try:
        do_something()
    except Exception:
        transaction.set_rollback(True)  # 手动回滚
        raise
相关推荐
袋鼠云数栈3 分钟前
实时湖仓如何真正做到“数据够新”?
大数据·数据库·人工智能·数据治理
ACP广源盛1392462567319 分钟前
M6/M5 Pro Mac mini 端侧 AI 落地@ACP#YLB3116 中端多盘存储扩展在 AI 服务中的机会与应用场景
大数据·网络·数据库·人工智能·嵌入式硬件·macos
ACP广源盛139246256731 小时前
M6/M5 Pro Mac mini 端侧 AI 新形态@ACP#GSV5800 Serdes 长距离视频传输在 AI 服务中的机会与落地场景
大数据·网络·数据库·人工智能·嵌入式硬件·macos·音视频
倔强的石头_3 小时前
事务边界与批量写入:避免长事务、锁等待和日志压力
数据库
努力努力再努力wz3 小时前
【Redis入门系列】从 KEYS 到 SCAN:渐进式遍历、Cursor 与位反转原理
数据库·redis·缓存
坐吃山猪3 小时前
【多线程】Lock与Condition
大数据·数据库
Lightpwd4 小时前
Spring Boot 多数据源落地:AbstractRoutingDataSource + 注解切面(附源码)
数据库·后端
LabVIEW开发4 小时前
LabVIEW 64位安装的位深陷阱:工具包、内存与工程兼容
数据库·labview·labview知识·labview功能·labview程序
寺中人4 小时前
MySQL 8.0 Windows 完整安装教程:环境配置、密码重置与常见报错排查
数据库·windows·mysql·环境搭建·mysql 安装
这个DBA有点耶4 小时前
同样48核配置TPS差1倍?高性价比数据库一体机的“软硬协同”才是分水岭
服务器·数据库·架构