中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

基于Django的樂觀鎖與悲觀鎖解決訂單并發問題詳解

發布時間:2020-09-21 03:36:36 來源:腳本之家 閱讀:259 作者:躬耕于數 欄目:開發技術

前言

訂單并發這個問題我想大家都是有一定認識的,這里我說一下我的一些淺見,我會盡可能的讓大家了解如何解決這類問題。

在解釋如何解決訂單并發問題之前,需要先了解一下什么是數據庫的事務。(我用的是mysql數據庫,這里以mysql為例)

1)     事務概念

一組mysql語句,要么執行,要么全不不執行。

 2)  mysql事務隔離級別

Read Committed(讀取提交內容)

如果是Django2.0以下的版本,需要去修改到這個隔離級別,不然樂觀鎖操作時無法讀取已經被修改的數據

RepeatableRead(可重讀)

這是這是Mysql默認的隔離級別,可以到mysql的配置文件中去修改;

transcation-isolation = READ-COMMITTED

在mysql配置文件中添加這行然后重啟mysql就可以將事務隔離級別修改至Read Committed

其他事務知識這里不會用到就不浪費時間去做介紹了。

悲觀鎖:開啟事務,然后給mysql的查詢語句最后加上for update。

這是在干什么呢。可能大家有些不理解,其實就是給資源加上和多線程中加互斥鎖一樣的東西,確保在一個事務結束之前,別的事務無法對該數據進行操作。

下面是悲觀鎖的代碼,加鎖和解鎖都是需要消耗CPU資源的,所以在訂單并發少的情況使用樂觀鎖會是一個更好的選擇。

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':'訂單創建成功'})

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

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

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

最好在使用樂觀鎖之前查看一下數據庫的隔離級別,mysql中查看事物隔離級別的命令為

select @@global.tx_isolation;

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

洮南市| 山东省| 南丰县| 富裕县| 东台市| 金坛市| 南江县| 双牌县| 合水县| 鹰潭市| 乌什县| 菏泽市| 永昌县| 滨州市| 台江县| 伽师县| 连云港市| 南部县| 玛纳斯县| 五常市| 阿城市| 穆棱市| 肥乡县| 五家渠市| 资讯| 阿克陶县| 筠连县| 江永县| 漳平市| 托克托县| 三门县| 旬阳县| 长泰县| 晋州市| 张家界市| 吉安县| 大关县| 辽阳市| 新宾| 土默特右旗| 武乡县|