django事务管理

2021年08月16日 未雨晴空 0评论 907阅读 0喜欢

django的事务管理

Django 的默认行为是在自动提交模式下运行。除非事务处于活动状态,否则每个查询都会立即提交到数据库。

Django 自动使用事务或保存点来保证需要多个查询的 ORM 操作的完整性,尤其是QuerySet的delete()update()的批量操作。如果想停用事务管理,则可以通过在其数据库的配置中设置AUTOCOMMIT=False为来完全禁用给定数据库的 Django 事务管理,如果这样做,Django 将不会启用自动提交,也不会执行任何提交。您将获得底层数据库库的常规行为。这要求您明确提交每个事务,即使是由 Django 或第三方库启动的事务。因此,这最好用于您想要运行自己的事务控制中间件或做一些非常奇怪的事情的情况。

transaction

Django 在django.db.transaction模块中提供了一些API来管理事务。

non_atomic_requests(using=None)

该函数是一个装饰器用来否定ATOMIC_REQUESTS给定视图的效果。

atomic(using=None, savepoint=True)

原子性是数据库事务的定义属性。atomic 允许我们创建一个代码块,在其中保证数据库的原子性。如果代码块成功完成,更改将提交到数据库。如果出现异常,则回滚更改。atomic块可以嵌套。在这种情况下,当内部块成功完成时,如果稍后在外部块中引发异常,其效果仍然可以回滚。有时确保atomic块始终是最外层的atomic块很有用,以确保在块退出时无错误地提交任何数据库更改。这称为耐久性。

get_autocommit(using=None)

获取连接的自动提交状态

set_autocommit(autocommit, using=None)

设置连接的自动提交状态

commit(using=None)

提交一个事务

rollback(using=None)

回滚一个事务

savepoint(using=None)

创建一个新的保存点。这标志着交易中已知处于“良好”状态的一个点。返回保存点 ID ( sid)。

savepoint_rollback(sid, using=None)

将事务回滚到 savepoint sid

savepoint_commit(sid, using=None)

释放保存点sid。自创建保存点以来执行的更改成为事务的一部分。

clean_savepoints(using=None)

重置用于生成唯一保存点 ID 的计数器。

get_rollback(using=None)

获取回滚标志

set_rollback(rollback, using=None)

设置回滚标志以True在退出最里面的原子块时强制回滚。这对于触发回滚而不引发异常可能很有用。

将其设置为False可防止此类回滚。在此之前,请确保您已将事务回滚到当前原子块中已知良好的保存点!否则你会破坏原子性并且可能会发生数据损坏。

on_commit(func, using=None)

有时需要在事务执行完成后执行一些其他操作,浙西操作可能包括异步任务任务、电子邮件通知或缓存失效。Django 提供了on_commit()注册回调函数的函数,这些回调函数应该在事务成功提交后执行:将任何函数(不带参数)传递给on_commit()但前提是该事务成功提交

from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    ...
    transaction.on_commit(lambda: some_celery_task.delay('arg1'))

您传入的函数将在被调用的假设数据库写入on_commit()成功提交后立即被调用。如果on_commit()在没有活动事务时调用,回调将立即执行。如果该假设的数据库写入被回滚(通常是在atomic()块中引发未处理的异常时),则您的函数将被丢弃并且永远不会被调用。

with transaction.atomic():  # 外层事务
    transaction.on_commit(foo)

    try:
        with transaction.atomic():  # 内侧事务,创建一个保存点
            transaction.on_commit(bar)
            raise SomeError()  # 抛出异常,丢弃这个保存点
    except SomeError:
        pass

# foo() 函数将会被调用,而bar()将不会被调用

如果在事务成功提交后执行回调函数,如果回调函数中执行失败不会导致事务回滚,如果需要回调操作失败意味着事务本身失败),这个时候就不能用on_commit()。on-commit 函数仅适用于自动提交模式和[atomic()]或ATOMIC_REQUESTS事务 API。on_commit()禁用自动提交并且您不在原子块内时调用将导致错误。

上面这些函数接受一个using参数,该参数应该是数据库的名称。如果未提供,Django 将使用"default"数据库。

事务应用

原子性是数据库事务的定义属性。atomic 允许我们创建一个代码块,在其中保证数据库的原子性。如果代码块成功完成,更改将提交到数据库。如果出现异常,则回滚更改。atomic块可以嵌套。在这种情况下,当内部块成功完成时,如果稍后在外部块中引发异常,其效果仍然可以回滚。

http请求开启事务

django支持在将每个请求包装在一个事务中。如果想要启动此行为,需要在settings.py的每个数据库配置中设置ATOMIC_REQUESTS=True,这一特性会这个特性将每个视图函数包装在@transaction.atomic装饰器中。在调用视图函数之前,Django 会启动一个事务。如果生成的响应没有问题,Django 将提交事务。如果视图产生异常,Django 会回滚事务。请注意,事务中仅包含视图的执行。中间件在事务之外运行,模板响应的呈现也是如此

from django.db import transaction

"""如果想要某个视图函数不自动开启事务可以使用下面的装饰器来关闭事务。"""
@transaction.non_atomic_requests
def view2(request):
    pass

从上面可以知道,django为自动给每个视图打开一个事务会占用一些性能开销,当请求数量很多的情况下,会产生性能问题(对性能的影响取决于应用程序的查询模式以及数据库处理锁定的程度),尽管上面的开启事务很简单。同时如果为了流响应(StreamingHttpResponse)请求开启事务也是不推荐的,因为读取响应的内容通常会执行代码来生成内容。由于视图已经返回,因此此类代码在事务之外运行。一般来说,不建议在生成流响应时写入数据库,因为在开始发送响应后没有处理错误的合理方法。

开启事务的两种方式

基于atomic装饰器

from django.db import transaction

@transaction.atomic
def view2(request):
    pass

基于atomic上下文管理器

from django.db import transaction

def view2(request):
    with transaction.atomic():
        pass

事务嵌套

from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()
    try:
        with transaction.atomic():
            generate_relationships()
    except IntegrityError:
        handle_exception()

    add_children()

上面的例子,第一层使用了atomic装饰器,第二层使用了atomic上下文管理器开启了两个事务。即使generate_relationships()通过破坏完整性约束导致数据库错误,由于加了异常捕捉处理,下面的add_children()仍然正常执行,同时和create_parent()仍然绑定到同一个事务。因为任何尝试的操作在被调用generate_relationships()时都已经安全回滚handle_exception(),因此异常处理程序也可以在必要时对数据库重新进行操作。

避免在内部捕获异常atomic

退出atomic块时,Django 会查看它是正常退出还是异常退出,以确定是提交还是回滚。如果您捕获并处理atomic块内的异常,您可能会向 Django 隐藏发生了问题的事实。这可能会导致意外行为。捕获数据库错误的正确方法是围绕一个atomic块,如上所示。如有必要,atomic为此添加一个额外的块。这种模式还有另一个优点:它明确界定了如果发生异常,哪些操作将被回滚。如果您捕获由原始 SQL 查询引发的异常,Django 的行为是未指定的并且依赖于数据库。

回滚事务时,您可能需要手动恢复模型状态。

当事务回滚发生时,模型字段的值不会被恢复。这可能会导致模型状态不一致,除非您手动恢复原始字段值。如下代码,更新对象的active = True,如果更新时事务失败,但是active仍然被认为是True,实际上不是,所以需要手动恢复模型的状态。

from django.db import DatabaseError, transaction

obj = MyModel(active=False)
obj.active = True
with transaction.atomic():
    obj.save()
if obj.active:
 pass
from django.db import DatabaseError, transaction

obj = MyModel(active=False)
obj.active = True
with transaction.atomic():
    obj.save()
try:
 with transaction.atomic():
     obj.save()
except DatabaseError:
 obj.active = False

if obj.active:
 ...

为了保证原子性,atomic禁用一些API。尝试在块内提交、回滚或更改数据库连接的自动提交状态atomic将引发异常。atomic接受一个using参数,它应该是数据库的名称。如果未提供此参数,Django 将使用"default" 数据库。在幕后,Django 的事务管理代码:

  • 进入最外层atomic区块时开启交易;
  • 进入内部atomic块时创建一个保存点;
  • 退出内部块时释放或回滚到保存点;
  • 退出最外层块时提交或回滚事务。

您可以通过将savepoint参数设置为 来禁用为内部块创建保存点 False。如果发生异常,Django 将在退出第一个带有保存点的父块时执行回滚(如果有),否则最外层块。原子性仍然由外部事务保证。仅当保存点的开销很明显时才应使用此选项。它具有破坏上述错误处理的缺点。您可以atomic在关闭自动提交时使用。它只会使用保存点,即使是最外面的块。

事务回滚

from django.db import transaction

@transaction.atomic
def viewfunc(request):
    a.save() 
    try:
        b.save()
    except IntegrityError:
        transaction.rollback()
    c.save()

调用transaction.rollback()回滚整个事务。任何未提交的数据库操作都将丢失。在此示例中,由 所做的更改a.save()将丢失,即使该操作本身没有引发错误。

保存点回滚

您可以使用保存点来控制回滚的范围。在执行可能失败的数据库操作之前,您可以设置或更新保存点;这样,如果操作失败,您可以回滚单个违规操作,而不是整个事务。例如:

a.save() 
sid = transaction.savepoint()
try:
    b.save() 
    transaction.savepoint_commit(sid)
except IntegrityError:
    transaction.savepoint_rollback(sid)
c.save()

在这个例子中,a.save()b.save()引发异常的情况下不会被撤消 。

发表评论 取消回复

电子邮件地址不会被公开。

请输入以http或https开头的URL,格式如:https://oneisall.top