乐观锁和悲观锁它们都是一种思想,都是人们定义出来的概念,和语言无关
并发控制:当程序出现并发的问题时,我们需要保证在并发情况下数据的准确性,以保证当前用户在和其他用户一起操作时,得到的结果和他单独操作时得到的结果是一样的,没有做好并发控制,就可能导致脏读、幻读、不可重复读等问题
悲观锁:当要对一条数据进行修改时,为了避免数据同时被其他人修改,最好的办法,最好的方法就是直接对该数据进行加锁,让并行变成串行,其他线程想要访问数据时需要阻塞挂起,互斥锁就属于悲观锁
乐观锁:总是假设最好的情况,每次拿数据都认为别人不会修改,所以不会上锁,只在更新的时候会判断以下在此期间别人有没有更新这个数据,如果更新了,我们就不会修改
乐观锁的实现:
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('秒杀成功')
事务执行成功之后还可以执行回调函数:
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('秒杀成功')
现在用代码实现悲观锁:只要在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('库存不足,秒杀失败')
悲观锁代码实现:思路是在拿到数据的时候先记录一下,然后在修改的时候加一条筛选条件,除了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