Django处理点餐订单的并发情况

        最近在写一个订餐微信小程序,考虑后台代码会修改数据库的菜品数量,多个用户同时点餐会发现菜品数量不一致的问题。后台订单的存储是用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)

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Django外卖点餐系统是一个基于Python和Django框架开发的复杂应用程序,通常包括多个模块,如用户管理、菜品管理、订单管理等。下面是实现的基本步骤: 1. **设置项目**: - 创建一个新的Django项目,并初始化相应的应用(如orders, menu, users等)。 2. **模型设计**: - 用户模型(User): 包括基本的用户信息,可能有注册、登录功能。 - 菜品模型(Product): 存储菜品信息,包括名称、描述、价格等。 - 菜单模型(Menu): 可能与特定餐厅关联,包含多个菜品的列表。 - 订单模型(Order): 存储用户订单信息,如订单号、创建时间、状态等。 3. **表单和视图**: - 用户注册和登录表单,用于处理用户认证。 - 菜品展示表单,显示在菜单中供用户选择。 - 订单提交视图,处理用户下单请求,将选中的菜品加入购物车,然后生成订单。 - 订单详情和状态更新视图,用户可以查看和修改订单信息。 4. **模板设计**: - 使用HTML和Django模板语言(Template Language)来设计界面,如登录页面、商品列表、订单确认页等。 5. **URL配置**: - 定义URL路由,将用户的行为映射到相应的视图函数。 6. **后端逻辑**: - 处理数据库操作,如添加菜品到订单、更新订单状态等。 - 可能涉及到库存管理,确保在用户下单时不会售罄。 7. **支付接口**: - 如果需要,集成第三方支付系统,以便用户在线完成支付。 8. **权限和验证**: - 保护用户隐私,如订单信息不对外公开,只有用户自己能看到。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值