悲观锁,乐观锁
一. 悲观锁
1. 原生mysql悲观锁
# 开启事务
begin;
# 行锁,如果不加锁,可能会出现卖超了
select * from book where id = 1 for update;
# order表中加数据---生成订单
insert into order。。。。。
# 扣减库存, 更新。
update book set count = count - 1 where id = 1;
# 提交事务
commit;
2. ORM 实现悲观锁(select_for_update())
使用悲观锁实现下单
# 整个过程在一个事物中---》改两个表:book表减库存,订单表生成记录
@transaction.atomic
def seckill(request):
# 锁住查询到的book对象,直到事务结束
sid = transaction.savepoint() # 保存点
# 悲观锁: select_for_update()
# 加锁了--> 行锁还是表锁? 分情况,都有可能
# 加悲观锁,行锁,锁住当前行
book = Book.objects.select_for_update().filter(pk=1).first()
if book.count > 0:
print('库存可以,下单')
# 订单表插入一条
Order.objects.create(order_id=str(datetime.datetime.now()), order_name='测试订单')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是,就回滚
time.sleep(random.randint(1, 4)) # 模拟延迟
book.count=book.count-1
book.save()
transaction.savepoint_commit(sid) # 提交,释放行锁
return HttpResponse('秒杀成功')
else:
transaction.savepoint_rollback(sid) #回滚,释放行锁
return HttpResponse('库存不足,秒杀失败')
二. 乐观锁(秒杀)
1. 普通版
因为乐观锁是通过特殊标识(版本)进行判断,当并发时,多个线程可能同时读取版本,此时版本是一样的,但是修改时再次判断版本时,发现版本被修改,就会秒杀失败,这样库存就不会被全部秒杀完, 因此普通版是有缺陷的。
@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('库存不足,秒杀失败')
2. 完整版
完整版是通过判断库存还有没有,让秒杀失败的通过 “while循环” 继续进行秒杀,直到库存不足,还没成功的就是真正失败。具体代码如下:
from django.http import HttpResponse
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from django.db import transaction
from seckill.get_result import get_result
from .models import Goods, Order
import datetime
class SeckillGoodsView(ViewSet):
@action(methods=['GET'], detail=False)
# 2 乐观锁秒杀--普通版
@transaction.atomic
def seckill(self, request):
while True:
# 锁住查询到的book对象,直到事务结束
sid = transaction.savepoint()
goods = Goods.objects.filter(pk=1).first() # 没加锁
count = goods.store
print('现在的库存为:%s' % count)
if goods.store > 0:
print('库存可以,下单')
Order.objects.create(order_id=str(datetime.datetime.now()), order_name='测试订单-乐观锁')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是,就回滚
# time.sleep(random.randint(1, 4)) # 模拟延迟
res = Goods.objects.filter(pk=1, store=count).update(store=count - 1)
if res >= 1: # 表示修改成功
transaction.savepoint_commit(sid)
print('秒杀成功')
return HttpResponse('秒杀成功')
else: # 修改不成功,回滚
transaction.savepoint_rollback(sid)
print('秒杀失败')
continue
else:
transaction.savepoint_rollback(sid)
return HttpResponse('库存不足,秒杀失败')
三. 异步(乐观锁)秒杀
效率最高的, 部分代码展示如下:
完整代码:https://gitee.com/codegjj/seckill
# -------------------------- 视图 views.py ---------------------------------
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from seckill.get_result import get_result
from .tasks import seckill_goods
class SeckillGoodsView(ViewSet):
@action(methods=['GET'], detail=False)
# 2 乐观锁秒杀--普通版
# @transaction.atomic
def seckill(self, request):
task_id = seckill_goods.delay()
print(task_id)
return Response({'task_id': str(task_id)})
# 获取结果
@action(methods=['GET'], detail=False)
def result(self, request):
task_id = request.query_params.get('task_id')
res = get_result(task_id)
return Response({'result': res})
# -------------------------- 任务 tasks.py ---------------------------------
from celery import shared_task
from .models import Goods, Order
from django.db import transaction
import datetime
@shared_task()
@transaction.atomic
def seckill_goods():
while True:
# 锁住查询到的book对象,直到事务结束
sid = transaction.savepoint()
goods = Goods.objects.filter(pk=1).first() # 没加锁
count = goods.store
print('现在的库存为:%s' % count)
if goods.store > 0:
print('库存可以,下单')
Order.objects.create(order_id=str(datetime.datetime.now()), order_name='测试订单-乐观锁')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是,就回滚
# time.sleep(random.randint(1, 4)) # 模拟延迟
res = Goods.objects.filter(pk=1, store=count).update(store=count - 1)
if res >= 1: # 表示修改成功
transaction.savepoint_commit(sid)
print('秒杀成功')
return '秒杀成功'
else: # 修改不成功,回滚
transaction.savepoint_rollback(sid)
print('秒杀失败')
continue
else:
transaction.savepoint_rollback(sid)
print('123')
return '库存不足,秒杀失败'