zoukankan      html  css  js  c++  java
  • Django 事务操作

    如何在Django中进行事务操作

    案例:

    客户A要给客户B转一笔钱,这个在数据库中需要进行两步:

      1.客户A减钱

      2.客户B加钱

    如果在第一步结束后,服务器出现异常,停下了,第二步没有进行,如果数据库使用了事务操作,真的出现异常的时候,前面的操作会进行回滚。

    简单的说就是:要么全部执行成功,要么一个都不执行。

    这个回滚的操作就叫做数据库的原子性操作。

    但是,这是在MySQL数据库中,而我们在Django的ORM中如何进行呢?

    显式控制事务

    Django提供了一个API来控制数据库事务。

    atomic(using = Nonesavepoint = True

    原子性是数据库事务的定义属性。atomic 允许我们创建一个代码块,在该代码块中保证数据库的原子性。如果代码块成功完成,则更改将提交到数据库。如果存在异常,则回滚更改。


    首先,我们要导入一个Django的内置模块

    from django.db import transaction

    接着,就可以使用了

    from django.db import transaction
    
    with transaction.atomic():
        # ORM操作
        pass

    我们举个例子测试一下

    方式一

    在"try/except"块中开启事务(且用到with上下文管理器)

    1.创建一个项目,新建一个APP(基础操作,这里不再赘述);

    2.通过ORM创建生成表:

    from django.db import models
    
    class UserInfo(models.Model):
        username = models.CharField("用户",max_length=32)
        balance = models.CharField("余额",max_length=32)

    注意啊:踩过的坑,涉及金融计算,涉及小数啊,要求特别精确的,我们用字符串存储。

    如果是金融计算的话,我们用一个Decimal来进行计算。

    3.我们给数据库加两条数据,用来模拟两个用户之间的转账;

    4.配置URL;

    5.创建对应的视图函数:

    from django.shortcuts import render,HttpResponse
    from app01 import models
    from django.db import transaction
    from django.db.models import F
    
    def index(request):
        try:
            with transaction.atomic():
                models.UserInfo.object.filter(id=1).update(balance=F("balance")-100)
                models.UserInfo.object.filter(id=2).update(balance=F("balance")+100)
        except Exception as e:
            return HttpResponse("出现错误<%s>"%str(e))
        return HttpResponse("执行成功")

    当我们访问index的时候,会进行一次转账操作。

    6.现在,我们让他报错:

    from django.shortcuts import render,HttpResponse
    from app01 import models
    from django.db import transaction
    from django.db.models import F
    
    def index(request):
        try:
            with transaction.atomic():
                models.UserInfo.object.filter(id=1).update(balance=F("balance")-100)
                raise '一个错误'
                models.UserInfo.object.filter(id=2).update(balance=F("balance")+100)
        except Exception as e:
            return HttpResponse("出现错误<%s>"%str(e))
        return HttpResponse("执行成功")

    我们再次查看数据库文件,如果没有数据的原子性操作,我们第一条sql执行完报错,那钱肯定是减去了。

    但是,我们进行的是原子性的操作,你会发现钱没有减诶。

    完美,没毛病

    这是常规的一种操作,另外还有一起其他的方式。

    方式二

    对整个view视图开启事务

    from django.db import transaction
    
    @transaction.atomic
    def index(request):
        # ORM操作
        return ....

    注意不要在原子块中进行错误捕获!

    当退出原子块的时候,Django会去查看它是否正常退出或者是否有异常来确定是否提交或者回滚。

    如果你捕获并处理了原子块中的异常,可能会隐藏Django中发生问题的事实。这样可能会造成非预期的行为。

    以上,程序中并没有显示调用commit()提交方法和rollback()回滚方法,原因是Django默认设置为自动提交。当然你可以改变这一切,Django为此提供了相关API:get_autocommit(using=None)set_autocommit(autocommit, using=None)

    为什么Django使用自动提交

    在SQL标准中,每个SQL查询都会启动一个事务,除非一个事务已经处于活动状态。然后必须显式提交或回滚此类事务。

    这对应用程序开发人员来说并不总是方便 为了缓解此问题,大多数数据库都提供自动提交模式。启用自动提交并且没有事务处于活动状态时,每个SQL查询都会包含在自己的事务中。换句话说,不仅每个这样的查询都会启动事务,而且事务也会自动提交或回滚,具体取决于查询是否成功。

    事务中的保存点

    保存点是事务中的标记,使您可以回滚部分事务,而不是完整事务。SQLite(≥3.6.8),PostgreSQL,Oracle和MySQL(使用InnoDB存储引擎时)后端可以使用保存点。其他后端提供了保存点功能,但它们是空操作 - 它们实际上并没有做任何事情。

    如果您使用自动提交(Django的默认行为),则保存点不是特别有用。但是,一旦打开事务atomic(),就会构建一系列等待提交或回滚的数据库操作。如果发出回滚,则回滚整个事务。保存点提供了执行细粒度回滚的功能,而不是将执行的完全回滚transaction.rollback()

    当atomic()装饰器嵌套时,它会创建一个保存点以允许部分提交或回滚。强烈建议您使用atomic()而不是下面描述的功能,但它们仍然是公共API的一部分,并且没有计划弃用它们。

    这些函数中的每一个都接受一个using参数,参数应该是行为适用的数据库的名称。如果未using提供参数,则使用"default"数据库。

    保存点由以下三个函数控制django.db.transaction:

    1)savepoint(using = None

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

    2)savepoint_commitsidusing = None

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

    3)savepoint_rollback(sidusing = None

    将事务回滚到保存点sid

    如果不支持保存点或数据库处于自动提交模式,则这些功能不执行任何操作。

    另外,还有一个实用功能:

    4)clean_savepoints(using = None

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

    以下示例演示了保存点的用法:

    from django.db import transaction
    
    # open a transaction
    @transaction.atomic
    def viewfunc(request):
    
        a.save()
        # transaction now contains a.save()
    
        sid = transaction.savepoint()
    
        b.save()
        # transaction now contains a.save() and b.save()
    
        if want_to_keep_b:
            transaction.savepoint_commit(sid)
            # open transaction still contains a.save() and b.save()
        else:
            transaction.savepoint_rollback(sid)
            # open transaction now contains only a.save()

    保存点可用于通过执行部分回滚从数据库错误中恢复。如果你在一个atomic()区块内执行此操作,整个区块仍将被回滚,因为它不知道你已经处理了较低级别的情况!为防止这种情况,您可以使用以下函数控制回滚行为。

    get_rollbackusing = None
    set_rollbackrollback,using = None

    设置回滚标志以True在退出最内层原子块时强制回滚。这可能有助于在不引发异常的情况下触发回滚。

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

      至此。转载请注明出处。

    本站相关链接:>>Django部署 ]

     

  • 相关阅读:
    【TIDB】2、TIDB进阶
    【TIDB】1、TiDb简介
    【Tair】淘宝分布式NOSQL框架:Tair
    【ElasticSearch】查询优化
    【高并发解决方案】9、大流量解决方案
    【高并发解决方案】8、Nginx/LVS/HAProxy负载均衡软件的优缺点详解
    【JVM】jdk1.8-jetty-swap被占满问题排查
    【JVM】记录一次线上SWAP偏高告警的故障分析过程
    【JVM】内存和SWAP问题
    【MySQL】mysql索引结构及其原理
  • 原文地址:https://www.cnblogs.com/wcwnina/p/9771825.html
Copyright © 2011-2022 走看看