1. 程式人生 > >基於Django的樂觀鎖與悲觀鎖解決訂單併發問題的一點淺見

基於Django的樂觀鎖與悲觀鎖解決訂單併發問題的一點淺見

然後就是樂觀鎖查詢了,相比悲觀鎖,樂觀鎖其實並不能稱為是鎖,那麼它是在做什麼事情呢。

其實是在你要進行資料庫操作時先去查詢一次資料庫中商品的庫存,然後在你要更新資料庫中商品庫存時,將你一開始查詢到的庫存數量和商品的ID一起作為更新的條件,當受影響行數返回為0時,說明沒有修改成功,那麼就是說別的程序修改了該資料,那麼你就可以回滾到之前沒有進行資料庫操作的時候,重新查詢,重複之前的操作一定次數,如果超過你設定的次數還是不能修改那麼就直接返回錯誤結果。

        該方法只適用於訂單併發較少的情況,如果失敗次數過多,會帶給使用者不良體驗,同時適用該方法要注意資料庫的隔離級別一定要設定為Read Committed 。

最好在使用樂觀鎖之前檢視一下資料庫的隔離級別,mysql中檢視事物隔離級別的命令為

select @@global.tx_isolation;

class OrderCommitView(View):
    """樂觀鎖"""
    # 開啟事務裝飾器
    @transaction.atomic
    def post(self,request):
        """訂單併發 ———— 樂觀鎖"""
        # 拿到id
        goods_ids_str = request.POST.get('goods_ids')
        goods_ids = goods_ids_str.split(',')
        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
        # 訂單運費
        transit_price = 10


        # 建立儲存點
        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,
            transit_price = transit_price
        )
        # 獲取redis連線
        goods = get_redis_goodsection('default')
        # 拼接key
        cart_key = 'cart_%d' % request.user.id








        for goods_id in goods_ids:
            # 嘗試查詢商品
            # 此處考慮訂單併發問題,


            # redis中取出商品數量
            count = goods.hget(cart_key, goods_id)
            if count is None:
                # 回滾到儲存點
                transaction.savepoint_rollback(sid)
                return JsonResponse({'res': 3, 'errmsg': '商品不在購物車中'})
            count = int(count)




            for i in range(3):
                # 若存在訂單併發則嘗試下單三次
                try:


                    goods = Goodsgoods.objects.get(id=goods_id)  # 不加鎖查詢
                    # goods = Goodsgoods.objects.select_for_update().get(id=goods_id)  # 加互斥鎖查詢
                except Goodsgoods.DoesNotExist:
                    # 回滾到儲存點
                    transaction.savepoint_rollback(sid)
                    return JsonResponse({'res':2,'errmsg':'商品資訊錯誤'})




                origin_stock = goods.stock


                print(origin_stock, 'stock')
                print(goods.id, 'id')


                if origin_stock < count:


                    # 回滾到儲存點
                    transaction.savepoint_rollback(sid)
                    return JsonResponse({'res':4,'errmsg':'庫存不足'})


                # # 商品銷量增加
                # goods.sales += count
                # # 商品庫存減少
                # goods.stock -= count
                # # 儲存到資料庫
                # goods.save()


                # 如果下單成功後的庫存
                new_stock = goods.stock - count
                new_sales = goods.sales + count


                res = Goodsgoods.objects.filter(stock=origin_stock,id=goods_id).update(stock=new_stock,sales=new_sales)
                print(res)
                if res == 0:
                    if i == 2:
                        # 回滾
                        transaction.savepoint_rollback(sid)
                        return JsonResponse({'res':5,'errmsg':'下單失敗'})
                    continue
                else:
                    break


            OrderGoods.objects.create(
                order = order_info,
                goods = goods,
                count = count,
                price = goods.price
            )


            # 刪除購物車中記錄
            goods.hdel(cart_key,goods_id)
            # 累加商品件數
            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.savepoint_commit(sid)






        return JsonResponse({'res':6,'errmsg':'訂單建立成功'})