乐观锁实现电商网站秒杀


from django_redis import get_redis_connection


class
OrderCommitView(view):
  @transaction.atomic
  def post(self, request):
    # 判断用户是否登录
    user = request.user
    if not user.is_authenticated():
      return JsonResponse("res":0,"error_msg":"用户未登录"
)
    
    addr_id = request.POST.get('addr_id')
    pay_method = request.POST.get('pay_methon')
    sku_ids = request.POST.get('sku_ids')
    
    if not all([addr_id, pay_method, sku_ids]):
      return JsonResponse({'res':1, 'error_msg':'params not enough'})

    # 校验支付方式
    if pay_method not in OrderInfo.PAY_METHODS.keys():
      return JsonResponse('ret':2,'error_msg':'非法支付方式')

    # 校验地址
    try:
      addr = Address.objects.get(id=addr_id)
    except Address.DoesNotExist:
      return JsonResponse('ret':3,'error_msg':"地址非法")

    # 创建订单核心业务
    订单id :日期加用户的id
    order_id = datetime.now().strftime("%Y%m%d%H%M%S")+str(user.id)

    # 运费
    transit_price = 10

    # 总数目总金额
    total_count = 0
    total_price = 0
    
    # 设置事物保存点
    save_id = transaction.savepoint()
    try:
      # todo 向df_oeder_info表中添加一条记录
      order = OrderInfo.objects.create(order_id=order,user=user,pay_method=pay_method,total_count=total_count,total_price=total_price)
      conn = get_redis_connection('default')
      cart_key = 'cart_%d'%user.id
      sku_ids = sku_ids.split(',')
      for sku_id in sku_ids:
        # 获取商品信息
        # for 循环3次 尝试都能下单成功
        for i in range(3):
          try:
            sku = GoodsSKU.objects.get(id=sku_id)
          except:
            # 商品不存在
            transaction.savepoint_rollback(save_id)
            return JsonResponse({'ret':4,'error_msg':"商品不存在"})
        

          # 从redis中获取用户需要购买的商品的数量
          count = conn.hget(cart_key,sku.stock)
          # todo 判断商品的库存
          if int(count) > sku.stock:
            transaction.savepoint_rollback(save_id)
            return JsonResponse({'res':6,'error_msg':'商品库存不足'})
          # todo 更新商品的库存和销量
          origin_stock = sku.stock
          new_stock = origin_stock - int(count)
          new_sales = sku.sales + int(count)
        
               print('user:%s,stock:%s'%(user.id,sku.stock))
          import time
          time.sleep(10)

  # upload df_goods_sku set stock=new_stock,sales=new_sales where id=sku_id and stock=origin_stock
   #乐观锁 res返回受影响的行数
          res = GoodsSKU.objects.filter(id=sku_id,stock=origin_stock).update(stock=new_stock.sales=new_sales)
          if res == 0:
            if i ==2:
              transactin.savepoint_rollback(save_id)
              return JsonResponse({'res':7,'error_msg':"下单失败2})
            continue
          # todo 向df_order_goods表中添加一条记录
          OrderGoods.objects.create(order=order,
                       sku=sku,count=count,price=sku.price)
          # todo 累加计算订单商品的总数量和总价格
          amount = sku.price*int(count)
          total_count += int(count)
          total_price += amount
          break
      # todo更新订单信息表中的商品的总数量和总价格
      order.total_count = total_count
      order.total_price = total_price
      order.save()
    except Exception as e:
      transaction.savepoint_rollback(save_id)
      return JsonResponse('res':7,'error_msg':'下单失败')
    # 提交事务
    transaction.savepoint_commit(save_id)
    # 提交事务
    conn.hdel(cart_key,*sku_ids)

    # 返回应答
    return JsonResponse({'res':5,'message':'创建成功'})


最后一步:

乐观锁需要将数据库(MYSQL)的默认隔离程度可重复读(读的是第一次读到的数据),所以需要将MySQL的隔离级别更改为读已提交
打开MySQL的配置文件 sudo vi /etc/mysql/mysqlconf.d/
修改为读已提交
transaction_isolation = READ_COMMITTED
重启MySQL:sudo service mysql restart

 

posted @ 2020-02-23 00:09  夜晚的潜水艇  阅读(258)  评论(0)    收藏  举报