首页 > 编程语言 >python-悲观锁和乐观锁

python-悲观锁和乐观锁

时间:2023-04-19 20:33:59浏览次数:34  
标签:count transaction python savepoint 乐观 book 悲观 sid HttpResponse

乐观锁和悲观锁它们都是一种思想,都是人们定义出来的概念,和语言无关
并发控制:当程序出现并发的问题时,我们需要保证在并发情况下数据的准确性,以保证当前用户在和其他用户一起操作时,得到的结果和他单独操作时得到的结果是一样的,没有做好并发控制,就可能导致脏读、幻读、不可重复读等问题
悲观锁:当要对一条数据进行修改时,为了避免数据同时被其他人修改,最好的办法,最好的方法就是直接对该数据进行加锁,让并行变成串行,其他线程想要访问数据时需要阻塞挂起,互斥锁就属于悲观锁
乐观锁:总是假设最好的情况,每次拿数据都认为别人不会修改,所以不会上锁,只在更新的时候会判断以下在此期间别人有没有更新这个数据,如果更新了,我们就不会修改
乐观锁的实现:
CAS:即比较并替换,是解决多线程并行情况下使用锁造成性能损耗的一种机制。CAS 操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B),执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。CAS会出现ABA的问题:比如我们准备对一个数据进行修改,这个数是1,在准备修改和提交的这段时间内,另一个事物已经将数字修改成了2,然后再修改为1,那么虽然我准备修改的数字是1,但是已经不是原来的那个1了。
解决ABA问题,我们需要对数据增加一个版本控制字段,只要有人动过这个数据,就将版本增加,修改一次版本是1,在修改一次版本是2,就说明这个数字有人动过。
版本号控制就是典型的CAS的应用:在数据表中增加一个版本号字段,每次更新数据时将版本号加1,同时将当前版本号作为更新条件,如果当前版本号与更新时的版本号一致,则更新成功,否则更新失败
使用场景:
并发量:如果并发量不大,可以使用悲观锁解决并发问题,如果并发量非常大的话,悲观锁会产生很严重的性能问题
响应速度:要求响应速度搞,建议使用乐观锁,成功就成功,失败就失败
冲突频率:冲突频率很高建议使用悲观锁
重试代价:重试代价大,建议使用悲观锁
读多写少:推荐使用乐观锁

django事务操作:

每个视图函数开启
from django.db import transaction
@transaction.atomic
def seckill(request):

局部开启
from django.db import transaction
def seckill(request):
	with transaction.atomic():
		pass
	return HttpResponse('秒杀成功')

保存点,回滚保存点Savepoint
设置回滚点:sid = transaction.savepoint()
提交(从回滚点开始):transaction.savepoint_commit(sid)
提交(整个事务):transaction.commit()
回滚到回滚点:transaction.savepoint_rollback(sid)   transaction.rollback()
事务提交后回调函数:transaction.on_commit(send_email)

django中事务的使用:

from django.shortcuts import render,HttpResponse
from django.db import transaction
from .models import Book,Order
# Create your views here.

def seckill(request):
    with transaction.atomic():
        sid = transaction.savepoint()
        print(sid)  # s96612_x1
        try:
            book = Book.objects.get(pk=1)
            book.name = '红楼梦'
            book.save()
            '''book表中有数据,order表中没有数据,所以try代码会报错,走道except会回滚,如果是order
            表中有数据,book表中书名会修改成功'''
            Order.objects.get(pk=1)
            '''从回滚点开始,提交事务'''
            transaction.savepoint_commit(sid)
        except Exception as e:
            transaction.savepoint_rollback(sid)
            print('出异常了,回滚')

    return HttpResponse('秒杀成功')

image
image
事务执行成功之后还可以执行回调函数:

from django.shortcuts import render,HttpResponse
from django.db import transaction
from .models import Book,Order
# Create your views here.

def send_email():
    print('发邮件了')

def seckill(request):
	'''开启事务'''
    with transaction.atomic():

        book = Book.objects.get(pk=1)
        book.count -= 1
        book.save()
        transaction.on_commit(send_email)

    return HttpResponse('秒杀成功')

image
现在用代码实现悲观锁:只要在orm中加入了select_for_update(),这一行代码就加上了锁,直到事务结束,用@transaction.atomic就是视图函数执行完毕事务才结束。由于我们的筛选条件是.filter(pk=1).first(),我们的悲观锁是行锁,这个事务只锁定这一行。如果我们查询的是表中所有数据(.all()),那么锁就变成了表锁。只要加了锁,线程执行就变成了串行。
使用场景:秒杀

import time

@transaction.atomic
def seckill(request):
    '''设置回滚点'''
    sid = transaction.savepoint()
    '''只要执行了select_for_update(),开启悲观锁,直到事务结束才释放锁(如果事务范围想控制小一些可以使用with)'''
    book_obj = Book.objects.select_for_update().filter(pk=1).first()
    time.sleep(4)
    if book_obj.count > 0:
        count = book_obj.count - 1
        book_obj.count = count
        book_obj.save()
        Order.objects.create(order_id='weyqwui',order_name='www')
        return HttpResponse('秒杀成功')
    else:
        return HttpResponse('库存不足,秒杀失败')

image
image

悲观锁代码实现:思路是在拿到数据的时候先记录一下,然后在修改的时候加一条筛选条件,除了pk或者name还应该加一条库存,如果筛选不到,回滚,继续修改。直到修改成功,或者库存为0秒杀失败:

@transaction.atomic
def seckill(request):
    # 锁住查询到的book对象,直到事务结束
    sid = transaction.savepoint()
    book = Book.objects.filter(pk=1).first()  # 没加锁
    count = book.count
    print('现在的库存为:%s' % count)
    if book.count > 0:
        print('库存可以,下单')
        Order.objects.create(order_id=str(datetime.datetime.now()), order_name='测试订单')
        # 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是,就回滚
        time.sleep(random.randint(1, 4))  # 模拟延迟
        res = Book.objects.filter(pk=1, count=count).update(count=count - 1)
        if res >= 1:  # 表示修改成功
            transaction.savepoint_commit(sid)
            return HttpResponse('秒杀成功')
        else:  # 修改不成功,回滚
            transaction.savepoint_rollback(sid)
            return HttpResponse('被别人改了,回滚,秒杀失败')

    else:
        transaction.savepoint_rollback(sid)
        return HttpResponse('库存不足,秒杀失败')

标签:count,transaction,python,savepoint,乐观,book,悲观,sid,HttpResponse
From: https://www.cnblogs.com/ERROR404Notfound/p/17334522.html

相关文章

  • 【中介者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
    简介中介者模式(MediatorPattern)是一种行为型模式。它限制对象之间的直接交互,它用一个中介对象来封装一系列的动作,以让对象之间进行交流。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。当一些对象和其他对象紧密耦合以致难以对其进......
  • 简单的python3脚本:从日志中提取信息
    命名:log_extractor.pyordownload_stats_extractor.py#coding:utf-8#!/usr/bin/python3deffilter_line(line,contains,contains_not):ifall(cinlineforcincontains)andnotany(ninlinefornincontains_not):ifint(line.split()[8])==2......
  • python-SSTI模板注入
    一、python_SSTI模板注入介绍ssti漏洞成因ssti服务端模板注入,ssti主要为python的一些框架jinja2makotornadodjango,PHP框架smartytwig,java框架jadevelocity等等使用了渲染函数时,由于代码不规范或信任了用户输入而导致了服务端模板注入,模板渲染其实并没有漏洞,主要是程序员......
  • 关于python中的class类犯得迷之错误
    昨天在写一个类,修了20几天假,忘了类的写法,刚开始我写成这样classA():def_init_():......然后我想把外面的一个方法c传进A类的方法b中,进行调用,就开始报错:object()takesnoparameters,刚开始很自信,不可能是自己写错,结果啪啪打脸,正确的类的写法是这样的......
  • python反序列化
    这篇文章介绍python反序列化。0X00前言本篇文章搬运大佬k0rz3n的研究文章,写的特别好,存下来学习一下。0X01Python的序列化和反序列化是什么Python的序列化和反序列化是将一个类对象向字节流转化从而进行存储和传输,然后使用的时候再将字节流转化回原始的对象的一个过程。1.......
  • Python操作MySQL就是这么简单
    Python操作MySQL就是这么简单下载MySQL8.0安装MySQL8.0步骤2:选择安装类型步骤3:选择安装位置步骤4:配置MySQL8.0步骤5:安装MySQL8.0步骤6:完成安装启动MySQL8.0MySQL8.0的常用命令python的pymysql库操作方法安装pymysql库安装pymysql库连接到MySQL数据库......
  • 数据分析的基本知识介绍,通过Python创建一些漂亮的数据可视化
    近一年势头不灭的Python在数据分析领域,是专家们的必备技能。随着IT行业的增长,对有经验的数据科学家的需求也水涨船高,而Python也一跃而成最受欢迎的语言。介绍数据分析的基本知识,并利用Python创建一些漂亮的数据可视化。概要为什么要学数据科学中的Python?Python简介为数据......
  • 零基础开始学Python,主要划分为哪四个阶段?
    零基础开始,Python要学到什么程度才能找到理想的工作呢,主要分为四个阶段1、第一阶段(一般岗位叫数据专员)基本学会excel(VBA最好学会;会做透视表;熟练用筛选、排序、公式),做好PPT。这样很多传统公司的数据专员已经可以做了2、第二阶段(数据专员~数据分析师)这一阶段要会SQL,懂业务,加上第一阶......
  • python当中如果有一个列表里面是多个字典,且有一对键值对相同,想要求不同的键对应的值之
    a=[{"id":1,"count":29},{"id':1,"count":39}]ids=list(set([u["id"]foruina]))#[1,]b=[]foridinids:count=0foruina:ifidinu.get('id'):count......
  • python当中的""和None,记一次小坑
    在模型类当中,比如定义了,可以传入空值:classPerson(models.Model):name=models.CharField(verbose_name='姓名',max_length=32)age=models.IntegerField(verbose_name='年龄',blank=True,null=True)在执行相应的增删改查中,如果age要传空值,则可......