最近在写一个订餐微信小程序,考虑后台代码会修改数据库的菜品数量,多个用户同时点餐会发现菜品数量不一致的问题。后台订单的存储是用clery来进行并行转串行,不会存在不一致的问题,餐厅同意之后再修改数据库也可以,但是会出现很多问题。
因此,对于订单的并发提交存储采用clery来做处理,在用户提交点餐的时候,用mysql数据库的乐观锁来处理菜品修改情况,在提交的同时修改菜品数量,保证用户看到的菜品数量一致,貌似还有很多问题。
悲观锁(并发量高):
当查询菜品数量时,数据库会加上一个X锁,保证只能读,不能写,锁住这一行之后其他用户点餐不能修改。这样出现死锁的几率会很高。使用类似如下语法
class OrderCommitView(View):
"""悲观锁"""
# 开启事务装饰器
@transaction.atomic
def post(self,request):
"""订单并发 ———— 悲观锁"""
# 拿到商品id
goods_ids = request.POST.getlist('goods_ids')
# 校验参数
if len(goods_ids) == 0 :
return JsonResponse({'res':0,'errmsg':'数据不完整'})
# 当前时间字符串
now_str = datetime.now().strftime('%Y%m%d%H%M%S')
# 订单编号
order_id = now_str + str(request.user.id)
# 地址
pay_method = request.POST.get('pay_method')
# 支付方式
address_id = request.POST.get('address_id')
try:
address = Address.objects.get(id=address_id)
except Address.DoesNotExist:
return JsonResponse({'res':1,'errmsg':'地址错误'})
# 商品数量
total_count = 0
# 商品总价
total_amount = 0
# 获取redis连接
conn = get_redis_connection('default')
# 拼接key
cart_key = 'cart_%d' % request.user.id
#
# 创建保存点
sid = transaction.savepoint()
order_info = OrderInfo.objects.create(
order_id = order_id,
user = request.user,
addr = address,
pay_method = pay_method,
total_count = total_count,
total_price = total_amount
)
for goods_id in goods_ids:
# 尝试查询商品
# 此处考虑订单并发问题,
try:
# goods = Goods.objects.get(id=goods_id) # 不加锁查询
goods = Goods.objects.select_for_update().get(id=goods_id) # 加互斥锁查询
except Goodsgoods.DoesNotExist:
# 回滚到保存点
transaction.rollback(sid)
return JsonResponse({'res':2,'errmsg':'商品信息错误'})
# 取出商品数量
count = conn.hget(cart_key,goods_id)
if count is None:
# 回滚到保存点
transaction.rollback(sid)
return JsonResponse({'res':3,'errmsg':'商品不在购物车中'})
count = int(count)
if goods.stock < count:
# 回滚到保存点
transaction.rollback(sid)
return JsonResponse({'res':4,'errmsg':'库存不足'})
# 商品销量增加
goods.sales += count
# 商品库存减少
goods.stock -= count
# 保存到数据库
goods.save()
OrderGoods.objects.create(
order = order_info,
goods = goods,
count = count,
price = goods.price
)
# 累加商品件数
total_count += count
# 累加商品总价
total_amount += (goods.price) * count
# 更新订单信息中的商品总件数
order_info.total_count = total_count
# 更新订单信息中的总价格
order_info.total_price = total_amount + order_info.transit_price
order_info.save()
# 事务提交
transaction.commit()
return JsonResponse({'res':5,'errmsg':'订单创建成功'})
乐观锁(并发量低):
乐观锁并不是真实存在的锁,而是在更新的时候确定数量是否被修改,如果相同,说明没人修改,可以进行减操作,如果不一致,就说明被其他用户修改了,没法进行操作。考虑到公司晚餐点餐用户在20-30人,出现并发的情况其实并不多,尝试使用乐观锁。
from django.http import HttpResponse
from rest_framework.generics import GenericAPIView
from app01.models import GoodsInfo
class Goods(GenericAPIView):
""" 购买商品 """
def post(self, request):
# 获取请求头中查询字符串数据
goods_id = request.GET.get('goods_id')
count = int(request.GET.get('count'))
while True:
# 查询商品对象
goods = GoodsInfo.objects.filter(id=goods_id).first()
# 获取原始库存
origin_stock = goods.stock
# 判断商品库存是否充足
if origin_stock < count:
return HttpResponse(content="商品库存不足", status=400)
# 演示并发请求
import time
time.sleep(5)
# 减少商品的库存数量,保存到数据库
# goods.stock = origin_stock - count
# goods.save()
""" 使用乐观锁进行处理,一步完成数据库的查询和更新 """
# update返回受影响的行数
result = GoodsInfo.objects.filter(id=goods.id, stock=origin_stock).update(stock=origin_stock - count)
if result == 0:
# 表示更新失败,有人抢先购买了商品,重新获取库存信息,判断库存
continue
# 表示购买成功,退出 while 循环
break
return HttpResponse(content="操作成功", status=200)