10.Django之ORM中的锁和事务

行锁

select_for_update(nowait=False, skip_locked=False) #注意必须用在事务里面

返回一个锁住行直到事务结束的查询集

entries = Entry.objects.select_for_update().filter(author=request.user)#加互斥锁,由于mysql在查询时自动加的是共享锁,所以我们可以手动加上互斥锁。create、update、delete操作时,mysql自动加行级互斥锁

所有匹配的行将被锁定,直到事务结束。这意味着可以通过锁防止数据被其它事务修改。

一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞,直到锁被释放。 如果这不想要使查询阻塞的话,使用select_for_update(nowait=True)

事务

全局开启事务

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mxshop',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': 'root',
        'OPTIONS': {
            "init_command": "SET default_storage_engine='INNODB'",
       #'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", #配置开启严格sql模式

        }
        "ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程
        "AUTOCOMMIT":False, #全局取消自动提交,慎用
    },
  'other':{
    'ENGINE': 'django.db.backends.mysql', 
            ......
  } #还可以配置其他数据库

}

如果想对某个http请求放水,可以用non_atomic_requests修饰器,那么他就不受事务的管控了

from django.db import transaction
@transaction.non_atomic_requests
def my_view(request):
    do_stuff()

def my_other_view(request):
    do_stuff_on_the_other_database()

局部使用事务

给函数做装饰器来使用

from django.db import transaction
@transaction.atomic
def viewfunc(request):
    do_stuff()

作为上下文管理器来使用,其实就是设置事务的保存点

from django.db import transaction

def viewfunc(request):
    do_stuff()
    with transaction.atomic(): #保存点
        do_more_stuff()      
    do_other_stuff()

一旦把atomic代码块放到try/except中,完整性错误就会被自然的处理掉了

from django.db import IntegrityError,transaction

def viewfunc(request):
    create_parent()  
    try:
        with transaction.atomic():
            generate.relationships()
    except IntegrityError:
        handle_exception() 
    add_children()

 还可以嵌套使用,函数的事务嵌套上下文管理器的事务,上下文管理器的事务嵌套上下文管理器的事务等

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块中的代码执行完的时候,Django会根据代码正常运行来执行相应的提交或者回滚操作。如果在atomic代码块里面捕捉并处理了异常,就有可能隐盖代码本身的错误,从而可能会有一些意料之外的不愉快事情发生。

  担心主要集中在DatabaseError和它的子类(如IntegrityError)。如果这种异常真的发生了,事务就会被破坏掉,而Django会在代码运行完后执行回滚操作。如果你试图在回滚前执行一些数据库操作,Django会抛出TransactionManagementError。通常你会在一个ORM相关的信号处理器抛出异常时遇到这个行为。

捕获异常的正确方式正如上面atomic代码块所示。如果有必要,添加额外的atomic代码块来做这件事情,也就是事务嵌套。这么做的好处是:当异常发生时,它能明确地告诉你那些操作需要回滚,而那些是不需要的。

    为了保证原子性,atomic还禁止了一些API。像试图提交、回滚事务,以及改变数据库连接的自动提交状态这些操  作,在atomic代码块中都是不予许的,否则就会抛出异常。

  下面是Django的事务管理代码:

  • 进入最外层atomic代码块时开启一个事务;
  • 进入内部atomic代码块时创建保存点;
  • 退出内部atomic时释放或回滚事务;注意如果有嵌套,内层的事务也是不会提交的,可以释放(正常结束)或者回滚
  • 退出最外层atomic代码块时提交或者回滚事务;

注意:transaction只对数据库层的操作进行事务管理,不能理解为python操作的事务管理

from django.db import IntegrityError,transaction
def example_view(request):
    tag = False
    with transaction.atomic():
        tag = True
        chang_obj()
        obj.save()
        raise DaataError
    print('tag = ',tag)

transation的其他方法

from django.db import IntegrityError,transaction
def viewfunc(request):
    a.save()
    sid = transaction.savepoint() #保存节点
    b.save()
    if want_to_keep_b:
        transaction.savepoint_commit(sid) #提交保存点
    else:
        transaction.savepoint_rollback(sid) #回滚保存点
    transaction.commit() #手动提交事务,默认是自动提交的,也就是说如果你没有设置取消自动提交,那么这句话不用写,如果你配置了那个AUTOCOMMIT=False,那么就需要自己手动进行提交

为保证事务的隔离性,我们还可以结合上面的锁来实现,也就是说在事务里面的查询语句,咱们使用select_for_update显示的加锁方式来保证隔离性,事务结束后才会释放这个锁

from django.db import IntegrityError,transaction
@transaction.atomic
def handle(self):
    try:
        user = User.objects.select_for_update().get(open_id=self.user.open_id)
    except User.DoesNotExist:
        raise BaseError(-1,"User does not exist.")

通过Django外部的python脚本来测试一下事务

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "moxing.settings")
    import django
    django.setup()
    
    import datetime
    from app01 import models
    
    try:
        from django.db import transaction
        with transaction.atomic():
            new_publisher = models.Publisher.objects.create(name='北京出版社')
            models.Book.objects.create(title='alex',publish_date = datetime.date.today(),publisher_id = 1)
    except Exception as e:
        print(str(e))

下面再说一些设置事务的小原则吧:

    1.保持事务短小
    2.尽量避免事务中rollback
    3.尽量避免savepoint
    4.默认情况下,依赖于悲观锁
    5.为吞吐量要求苛刻的事务考虑乐观锁
    6.显示声明打开事务
    7.锁的行越少越好,锁的时间越短越好

posted @ 2019-03-04 21:28  等待の喵  阅读(600)  评论(0编辑  收藏  举报