• 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':'创建成功'})
  • 相关阅读:
    使用Property+使用构造+构建自定义类+使用List<自定义类>(项目)
    几乎全面的食品英文总结
    网络资源深入剖析Binding(学习)
    WPF入门知识(学习)
    Get children gameobjects array and assign the script inside
    Unity3D + C#: Cloning the Content of a Serializable Class
    Use Web Fonts and the @fontface CSS Method
    Good XAML Patterns
    Coroutines & Yield
    How to make a mini map for your scene in Unity3d
  • 原文地址:https://www.cnblogs.com/yifengs/p/11672853.html
Copyright © 2020-2023  润新知