欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Django的樂(lè)觀鎖與悲觀鎖解決訂單并發(fā)問(wèn)題詳解

 更新時(shí)間:2019年07月31日 10:33:33   作者:躬耕于數(shù)  
這篇文章主要介紹了基于Django的樂(lè)觀鎖與悲觀鎖解決訂單并發(fā)問(wèn)題詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

前言

訂單并發(fā)這個(gè)問(wèn)題我想大家都是有一定認(rèn)識(shí)的,這里我說(shuō)一下我的一些淺見(jiàn),我會(huì)盡可能的讓大家了解如何解決這類問(wèn)題。

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

1)     事務(wù)概念

一組mysql語(yǔ)句,要么執(zhí)行,要么全不不執(zhí)行。

 2)  mysql事務(wù)隔離級(jí)別

Read Committed(讀取提交內(nèi)容)

如果是Django2.0以下的版本,需要去修改到這個(gè)隔離級(jí)別,不然樂(lè)觀鎖操作時(shí)無(wú)法讀取已經(jīng)被修改的數(shù)據(jù)

RepeatableRead(可重讀)

這是這是Mysql默認(rèn)的隔離級(jí)別,可以到mysql的配置文件中去修改;

transcation-isolation = READ-COMMITTED

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

其他事務(wù)知識(shí)這里不會(huì)用到就不浪費(fèi)時(shí)間去做介紹了。

悲觀鎖:開(kāi)啟事務(wù),然后給mysql的查詢語(yǔ)句最后加上for update。

這是在干什么呢。可能大家有些不理解,其實(shí)就是給資源加上和多線程中加互斥鎖一樣的東西,確保在一個(gè)事務(wù)結(jié)束之前,別的事務(wù)無(wú)法對(duì)該數(shù)據(jù)進(jìn)行操作。

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

class OrderCommitView(View):
  """悲觀鎖"""
  # 開(kāi)啟事務(wù)裝飾器
  @transaction.atomic
  def post(self,request):
    """訂單并發(fā) ———— 悲觀鎖"""
    # 拿到商品id
    goods_ids = request.POST.getlist('goods_ids')
 
    # 校驗(yàn)參數(shù)
    if len(goods_ids) == 0 :
      return JsonResponse({'res':0,'errmsg':'數(shù)據(jù)不完整'}) 
    # 當(dāng)前時(shí)間字符串
    now_str = datetime.now().strftime('%Y%m%d%H%M%S') 
    # 訂單編號(hào)
    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':'地址錯(cuò)誤'}) 
    # 商品數(shù)量
    total_count = 0
    # 商品總價(jià)
    total_amount = 0 
     # 獲取redis連接
    conn = get_redis_connection('default')
    # 拼接key
    cart_key = 'cart_%d' % request.user.id  
    #
    # 創(chuàng)建保存點(diǎn)
    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:
      # 嘗試查詢商品
      # 此處考慮訂單并發(fā)問(wèn)題,
      try:
        # goods = Goods.objects.get(id=goods_id) # 不加鎖查詢
        goods = Goods.objects.select_for_update().get(id=goods_id) # 加互斥鎖查詢
      except Goodsgoods.DoesNotExist:
        # 回滾到保存點(diǎn)
        transaction.rollback(sid)
        return JsonResponse({'res':2,'errmsg':'商品信息錯(cuò)誤'})
      # 取出商品數(shù)量
      count = conn.hget(cart_key,goods_id)
      if count is None:
        # 回滾到保存點(diǎn)
        transaction.rollback(sid)
        return JsonResponse({'res':3,'errmsg':'商品不在購(gòu)物車中'}) 
      count = int(count) 
      if goods.stock < count:
        # 回滾到保存點(diǎn)
        transaction.rollback(sid)
        return JsonResponse({'res':4,'errmsg':'庫(kù)存不足'}) 
      # 商品銷量增加
      goods.sales += count
      # 商品庫(kù)存減少
      goods.stock -= count
      # 保存到數(shù)據(jù)庫(kù)
      goods.save() 
      OrderGoods.objects.create(
        order = order_info,
        goods = goods,
        count = count,
        price = goods.price
      ) 
      # 累加商品件數(shù)
      total_count += count
      # 累加商品總價(jià)
      total_amount += (goods.price) * count 
    # 更新訂單信息中的商品總件數(shù)
    order_info.total_count = total_count
    # 更新訂單信息中的總價(jià)格
    order_info.total_price = total_amount + order_info.transit_price
    order_info.save()
 
    # 事務(wù)提交
    transaction.commit() 
    return JsonResponse({'res':5,'errmsg':'訂單創(chuàng)建成功'})

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

其實(shí)是在你要進(jìn)行數(shù)據(jù)庫(kù)操作時(shí)先去查詢一次數(shù)據(jù)庫(kù)中商品的庫(kù)存,然后在你要更新數(shù)據(jù)庫(kù)中商品庫(kù)存時(shí),將你一開(kāi)始查詢到的庫(kù)存數(shù)量和商品的ID一起作為更新的條件,當(dāng)受影響行數(shù)返回為0時(shí),說(shuō)明沒(méi)有修改成功,那么就是說(shuō)別的進(jìn)程修改了該數(shù)據(jù),那么你就可以回滾到之前沒(méi)有進(jìn)行數(shù)據(jù)庫(kù)操作的時(shí)候,重新查詢,重復(fù)之前的操作一定次數(shù),如果超過(guò)你設(shè)置的次數(shù)還是不能修改那么就直接返回錯(cuò)誤結(jié)果。

該方法只適用于訂單并發(fā)較少的情況,如果失敗次數(shù)過(guò)多,會(huì)帶給用戶不良體驗(yàn),同時(shí)適用該方法要注意數(shù)據(jù)庫(kù)的隔離級(jí)別一定要設(shè)置為Read Committed 。

最好在使用樂(lè)觀鎖之前查看一下數(shù)據(jù)庫(kù)的隔離級(jí)別,mysql中查看事物隔離級(jí)別的命令為

select @@global.tx_isolation;

class OrderCommitView(View):
  """樂(lè)觀鎖"""
  # 開(kāi)啟事務(wù)裝飾器
  @transaction.atomic
  def post(self,request):
    """訂單并發(fā) ———— 樂(lè)觀鎖"""
    # 拿到id
    goods_ids = request.POST.get('goods_ids')
    
    if len(goods_ids) == 0 :
      return JsonResponse({'res':0,'errmsg':'數(shù)據(jù)不完整'})
    # 當(dāng)前時(shí)間字符串
    now_str = datetime.now().strftime('%Y%m%d%H%M%S')
    # 訂單編號(hào)
    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':'地址錯(cuò)誤'}) 
    # 商品數(shù)量
    total_count = 0
    # 商品總價(jià)
    total_amount = 0
    # 訂單運(yùn)費(fèi)
    transit_price = 10 
    # 創(chuàng)建保存點(diǎn)
    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:
      # 嘗試查詢商品
      # 此處考慮訂單并發(fā)問(wèn)題, 
      # redis中取出商品數(shù)量
      count = goods.hget(cart_key, goods_id)
      if count is None:
        # 回滾到保存點(diǎn)
        transaction.savepoint_rollback(sid)
        return JsonResponse({'res': 3, 'errmsg': '商品不在購(gòu)物車中'})
      count = int(count) 
      for i in range(3):
        # 若存在訂單并發(fā)則嘗試下單三次
        try:
 
          goods = Goodsgoods.objects.get(id=goods_id) # 不加鎖查詢
          # goods = Goodsgoods.objects.select_for_update().get(id=goods_id) # 加互斥鎖查詢
        except Goodsgoods.DoesNotExist:
          # 回滾到保存點(diǎn)
          transaction.savepoint_rollback(sid)
          return JsonResponse({'res':2,'errmsg':'商品信息錯(cuò)誤'}) 
        origin_stock = goods.stock
        print(origin_stock, 'stock')
        print(goods.id, 'id') 
        if origin_stock < count: 
          # 回滾到保存點(diǎn)
          transaction.savepoint_rollback(sid)
          return JsonResponse({'res':4,'errmsg':'庫(kù)存不足'}) 
 
        # # 商品銷量增加
        # goods.sales += count
        # # 商品庫(kù)存減少
        # goods.stock -= count
        # # 保存到數(shù)據(jù)庫(kù)
        # goods.save() 
        # 如果下單成功后的庫(kù)存
        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
      ) 
      # 刪除購(gòu)物車中記錄
      goods.hdel(cart_key,goods_id)
      # 累加商品件數(shù)
      total_count += count
      # 累加商品總價(jià)
      total_amount += (goods.price) * count 
    # 更新訂單信息中的商品總件數(shù)
    order_info.total_count = total_count
    # 更新訂單信息中的總價(jià)格
    order_info.total_price = total_amount + order_info.transit_price
    order_info.save() 
    # 事務(wù)提交
    transaction.savepoint_commit(sid) 
    return JsonResponse({'res':6,'errmsg':'訂單創(chuàng)建成功'})

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Python爬蟲(chóng)實(shí)現(xiàn)爬取百度百科詞條功能實(shí)例

    Python爬蟲(chóng)實(shí)現(xiàn)爬取百度百科詞條功能實(shí)例

    這篇文章主要介紹了Python爬蟲(chóng)實(shí)現(xiàn)爬取百度百科詞條功能,結(jié)合完整實(shí)例形式分析了Python爬蟲(chóng)的基本原理及爬取百度百科詞條的步驟、網(wǎng)頁(yè)下載、解析、數(shù)據(jù)輸出等相關(guān)操作技巧,需要的朋友可以參考下
    2019-04-04
  • Python實(shí)現(xiàn)語(yǔ)音合成功能詳解

    Python實(shí)現(xiàn)語(yǔ)音合成功能詳解

    這篇文章主要為大家介紹了一個(gè)通過(guò)Python制作的小工具,可以實(shí)現(xiàn)語(yǔ)音識(shí)別以及文字轉(zhuǎn)語(yǔ)音的功能,文中的實(shí)現(xiàn)步驟講解詳細(xì),感興趣的可以動(dòng)手試一試
    2022-01-01
  • Python實(shí)現(xiàn)常見(jiàn)坐標(biāo)系的相互轉(zhuǎn)換

    Python實(shí)現(xiàn)常見(jiàn)坐標(biāo)系的相互轉(zhuǎn)換

    WGS84坐標(biāo)系、GCJ02坐標(biāo)系、BD09坐標(biāo)系和Web?墨卡托投影坐標(biāo)系是我們常見(jiàn)的四個(gè)坐標(biāo)系。這篇文章為大家整理了這四個(gè)坐標(biāo)系之間相互轉(zhuǎn)換的方法,需要的可以參考一下
    2023-02-02
  • Python中數(shù)組切片的用法實(shí)例詳解

    Python中數(shù)組切片的用法實(shí)例詳解

    python的數(shù)組切片操作很強(qiáng)大,但有些細(xì)節(jié)老是忘,故寫一點(diǎn)東西記錄下來(lái),下面這篇文章主要給大家介紹了關(guān)于Python中數(shù)組切片用法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • fastcgi文件讀取漏洞之python掃描腳本

    fastcgi文件讀取漏洞之python掃描腳本

    這篇文章主要介紹了fastcgi文件讀取漏洞之python掃描腳本,需要的朋友可以參考下
    2017-04-04
  • 深入理解Python虛擬機(jī)中魔術(shù)方法的使用

    深入理解Python虛擬機(jī)中魔術(shù)方法的使用

    這篇文章主要給大家介紹在?cpython?當(dāng)中一些比較花里胡哨的魔術(shù)方法,以幫助我們自己實(shí)現(xiàn)比較花哨的功能,當(dāng)然這其中也包含一些也非常實(shí)用的魔術(shù)方法,需要的可以參考下
    2023-05-05
  • Python機(jī)器學(xué)習(xí)庫(kù)scikit-learn使用詳解

    Python機(jī)器學(xué)習(xí)庫(kù)scikit-learn使用詳解

    scikit-learn是Python中最流行的機(jī)器學(xué)習(xí)庫(kù)之一,它提供了各種各樣的機(jī)器學(xué)習(xí)算法和工具,包括分類、回歸、聚類、降維等
    2023-03-03
  • Python常見(jiàn)文件操作的示例詳解

    Python常見(jiàn)文件操作的示例詳解

    文件操作是我們開(kāi)發(fā)中必不可少的一項(xiàng)需求。本文主要給大家介紹了關(guān)于Python常見(jiàn)的一些文件操作,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • 詳解Python的Django框架中的通用視圖

    詳解Python的Django框架中的通用視圖

    這篇文章主要介紹了詳解Python的Django框架中的通用視圖,是為MVC架構(gòu)的Django框架下的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-05-05
  • Python 閉包,函數(shù)分隔作用域,nonlocal聲明非局部變量操作示例

    Python 閉包,函數(shù)分隔作用域,nonlocal聲明非局部變量操作示例

    這篇文章主要介紹了Python 閉包,函數(shù)分隔作用域,nonlocal聲明非局部變量操作,結(jié)合實(shí)例形式分析了Python閉包及閉包中的變量聲明相關(guān)操作技巧,需要的朋友可以參考下
    2019-10-10

最新評(píng)論