订单并发这个问题我想大家都是有一定认识的,这里我说一下我的一些浅见,我会尽可能的让大家了解如何解决这类问题。

在解释如何解决订单并发问题之前,需要先了解一下什么是数据库的事务。(我用的是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':'订单创建成功'})

然后就是乐观锁查询了,相比悲观锁,乐观锁其实并不能称为是锁,那么它是在做什么事情呢。


其实是在你要进行数据库操作时先去查询一次数据库中商品的库存,然后在你要更新数据库中商品库存时,将你一开始查询到的库存数量和商品的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':'订单创建成功'})

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信