django-訂單並發處理--悲觀鎖和樂觀鎖


沖突比較少的時候,使用樂觀鎖。

沖突比較多的時候,使用悲觀鎖。

(1)     悲觀鎖

select * from df_goods_sku where id=17 for update;

 

悲觀鎖獲取數據時對數據行了鎖定,其他事務要想獲取鎖,必須等原事務結束。

視圖函數views.py

from django.db import transaction  # 事務處理
class OrderCommitView(View):
    '''訂單創建'''
    @transaction.atomic()   # 事務處理裝飾器
    def post(self, request):
        '''訂單創建'''
        # 判斷用戶是否登錄
        user = request.user
        if not user.is_authenticated():
            # 用戶未登錄
            return JsonResponse({'res':0, 'errmsg':'用戶未登錄'})

        # 接收參數
        addr_id = request.POST.get('addr_id')
        pay_method = request.POST.get('pay_method')
        sku_ids = request.POST.get('sku_ids') # 1,3

        # 校驗參數
        if not all([addr_id, pay_method, sku_ids]):
            return JsonResponse({'res':1, 'errmsg':'參數不完整'})

        # 校驗支付方式
        if pay_method not in OrderInfo.PAY_METHODS.keys():  # 需要在orders/model.py中添加PAY_METHODS字典
            return JsonResponse({'res':2, 'errmsg':'非法的支付方式'})

        # 校驗地址
        try:
            addr = Address.objects.get(id=addr_id)
        except Address.DoesNotExist:
            # 地址不存在
            return JsonResponse({'res':3, 'errmsg':'地址非法'})

        # todo: 創建訂單核心業務

        # 組織參數
        # 訂單id: 20171122181630+用戶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_order_info表中添加一條記錄
            order = OrderInfo.objects.create(order_id=order_id,
                                             user=user,
                                             addr=addr,
                                             pay_method=pay_method,
                                             total_count=total_count,
                                             total_price=total_price,
                                             transit_price=transit_price)

            # todo: 用戶的訂單中有幾個商品,需要向df_order_goods表中加入幾條記錄
            conn = get_redis_connection('default')
            cart_key = 'cart_%d'%user.id

            sku_ids = sku_ids.split(',')
            for sku_id in sku_ids:
                # 獲取商品的信息
                try:
                    # 悲觀鎖
                    # select * from df_goods_sku where id=sku_id for update;
                    sku = GoodsSKU.objects.select_for_update().get(id=sku_id)
                except:
                    # 商品不存在
                    transaction.savepoint_rollback(save_id)  # 回滾到保存點
                    return JsonResponse({'res':4, 'errmsg':'商品不存在'})

                # 查看誰拿到鎖
                print('user:%d stock:%d'%(user.id,sku.stock))

                # 從redis中獲取用戶所要購買的商品的數量
                count = conn.hget(cart_key, sku_id)

                # todo: 判斷商品的庫存
                if int(count) > sku.stock:
                    transaction.savepoint_rollback(save_id)  # 回滾到保存點
                    return JsonResponse({'res': 6, 'errmsg': '商品庫存不足'})

                # todo: 向df_order_goods表中添加一條記錄
                OrderGoods.objects.create(order=order,
                                          sku=sku,
                                          count=count,
                                          price=sku.price)

                import time
                time.sleep(10)
                # todo: 更新商品的庫存和銷量
                sku.stock -= int(count)
                sku.sales += int(count)
                sku.save()

                # todo: 累加計算訂單商品的總數量和總價格
                amount = sku.price*int(count)
                total_count += int(count)
                total_price += amount

            # 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, 'errmsg': '下單失敗'})

        # 如果沒失敗則提交事務
        transaction.savepoint_commit(save_id)
        # todo: 清除用戶購物車中對應的記錄
        conn.hdel(cart_key, *sku_ids)

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

(2) 樂觀鎖

查詢時不鎖數據,提交更改時進行判斷.

update df_goods_sku set stock=0, sales=1 where id=17 and stock=1;

使用樂觀鎖前,要先 設置mysql事務的隔離級別

打開mysql配置文件: sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

添加下面一行代碼

transaction-isolation = READ-COMMITTED

保存配置文件,重啟mysql服務。

 sudo service mysql restart

 實例views.py

class OrderCommitView(View):
    '''訂單創建'''
    @transaction.atomic()   # 事務處理裝飾器
    def post(self, request):
        '''訂單創建'''
        # 判斷用戶是否登錄
        user = request.user
        if not user.is_authenticated():
            # 用戶未登錄
            return JsonResponse({'res':0, 'errmsg':'用戶未登錄'})

        # 接收參數
        addr_id = request.POST.get('addr_id')
        pay_method = request.POST.get('pay_method')
        sku_ids = request.POST.get('sku_ids') # 1,3

        # 校驗參數
        if not all([addr_id, pay_method, sku_ids]):
            return JsonResponse({'res':1, 'errmsg':'參數不完整'})

        # 校驗支付方式
        if pay_method not in OrderInfo.PAY_METHODS.keys():  # 需要在orders/model.py中添加PAY_METHODS字典
            return JsonResponse({'res':2, 'errmsg':'非法的支付方式'})

        # 校驗地址
        try:
            addr = Address.objects.get(id=addr_id)
        except Address.DoesNotExist:
            # 地址不存在
            return JsonResponse({'res':3, 'errmsg':'地址非法'})

        # todo: 創建訂單核心業務

        # 組織參數
        # 訂單id: 20171122181630+用戶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_order_info表中添加一條記錄
            order = OrderInfo.objects.create(order_id=order_id,
                                             user=user,
                                             addr=addr,
                                             pay_method=pay_method,
                                             total_count=total_count,
                                             total_price=total_price,
                                             transit_price=transit_price)

            # todo: 用戶的訂單中有幾個商品,需要向df_order_goods表中加入幾條記錄
            conn = get_redis_connection('default')
            cart_key = 'cart_%d'%user.id

            sku_ids = sku_ids.split(',')
            for sku_id in sku_ids:
                for i in range(3):  # 多次嘗試
                    # 獲取商品的信息
                    try:
                        sku = GoodsSKU.objects.select_for_update().get(id=sku_id)
                    except:
                        # 商品不存在
                        transaction.savepoint_rollback(save_id)  # 回滾到保存點
                        return JsonResponse({'res':4, 'errmsg':'商品不存在'})

                    # 查看誰拿到鎖
                    print('user:%d stock:%d'%(user.id,sku.stock))

                    # 從redis中獲取用戶所要購買的商品的數量
                    count = conn.hget(cart_key, sku_id)

                    # todo: 判斷商品的庫存
                    if int(count) > sku.stock:
                        transaction.savepoint_rollback(save_id)  # 回滾到保存點
                        return JsonResponse({'res': 6, 'errmsg': '商品庫存不足'})

                    # todo: 更新商品的庫存和銷量
                    orgin_stock = sku.stock  # 原有的庫存
                    new_stock = orgin_stock - int(count)  # 要修改的庫存
                    new_sales = sku.sales + int(count)  # 要修改的銷量

                    # 返回受影響行數 查詢是否成功 放在添加記錄前先檢測再添加 --樂觀鎖
                    res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock).update(stock=new_stock, sales=new_sales)
                    if res == 0:
                        if i == 2:
                            # 嘗試的第3次,失敗了回滾
                            transaction.savepoint_rollback(save_id)
                            return JsonResponse({'res': 7, 'errmsg': '下單失敗'})
                        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, 'errmsg': '下單失敗7'})

        # 如果沒失敗則提交事務
        transaction.savepoint_commit(save_id)
        # todo: 清除用戶購物車中對應的記錄
        conn.hdel(cart_key, *sku_ids)

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM