Django项目实战----生成订单时高并发问题使用乐观锁


用户生成订单时出现的高并发问题

  1. 如果我们的商城有足够多的用户,在相差0.00000几秒的情况下基本上同时下单,这样先查询商品库存,再修改商品库存,的时候就会出现资源竞争问题

画图理解:
在这里插入图片描述


解决方案

  • 悲观锁

      当查询某条记录时,即让数据库为该记录加锁,锁住记录后别人无法操作,使用类似如下语法
      
      select stock from tb_sku where id=1 for update;
      
      SKU.objects.select_for_update().get(id=1)
      悲观锁类似于我们在多线程资源竞争时添加的互斥锁,容易出现死锁现象,采用不多。
    
  • 乐观锁

      乐观锁并不是真实存在的锁,而是在更新的时候判断此时的库存是否是之前查询出的库存,如果相同,表示没人修改,可以更新库存,否则表示别人抢过资源,不再执行库存更新。类似如下操作
      
      update tb_sku set stock=2 where id=1 and stock=7;
      
      SKU.objects.filter(id=1, stock=7).update(stock=2)
    
  • 任务队列

      将下单的逻辑放到任务队列中(如celery),将并行转为串行,所有人排队下单。比如开启只有一个进程的Celery,一个订单一个订单的处理。
    

乐观锁代码处理

  • 流程

      1. 创建死循环一直检测是否还有库存
      2. 记录数据库中的库存和销量
      3. 计算购买后的库存和销量
      4. 根据原库存数据来判断中间是否出现了高并发问题
      	4.1 如果查询出数据就修改
      	4.2 查询不出就跳过本次循环继续检测
      5. 查询出数据修改完之后break结束死循环
    
            # 遍历商品列表
            for sku in skus:
                # 添加死循环  一直来检测商品库存是否还有
                while True:
                    # 记录原数据库中商品的库存和销量
                    origin_stock = sku.stock
                    origin_sales = sku.sales
                    # 算出每一个商品的数量是否大于库存
                    count = new_cart_dict[sku.id]
                    if count > sku.stock:
                        # 库存不足返回到回滚点
                        transaction.savepoint_rollback(save_id)
                        return JsonResponse({'code': 400, 'errmsg': '库存不足'})
                    # 计算库存和销量
                    new_stock = origin_stock - count
                    new_sales = origin_sales + count
                    # time.sleep(5)
                    # 如果库存与保存库存的数据相等就说明中间没有人买同一个商品  过滤出数据再去修改库存和销量
                    # 结果返回的是过滤出的数据  有一条就返回1
                    result = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
                    # 如果没有过滤出数据就说明库存与一开始不同了  数据改变了  跳过本次循环  继续执行
                    if result == 0:
                        continue

                    # 库存减去购买的数量
                    # sku.stock -= count
                    # 销量加上购买的数量
                    # sku.sales += count
                    # 保存到数据库
                    # sku.save()

                    try:
                        # 保存订单商品
                        OrderGoods.objects.create(
                            order_id=order_id,
                            sku=sku,
                            count=count,
                            price=sku.price
                        )
                    except Exception as e:
                        print(e)
                        # 保存失败返回回滚点
                        transaction.savepoint_rollback(save_id)
                        return JsonResponse({'code': 400, 'errmsg': '保存失败'})

                    # 订单总数量+=每一个商品的数量  总价+=每一个商品的总价
                    order.total_count += count
                    order.total_amount += count * sku.price
                    # 如果执行下来没有continue就结束死循环来保存数据库
                    break

            # 总价+=运费
            order.total_amount += order.freight
            # 保存到数据库
            order.save()

修改MySQL的隔离级别

  • 事务隔离级别指的是在处理同一个数据的多个事务中,一个事务修改数据后,其他事务何时能看到修改后的结果。

  • MySQL数据库事务隔离级别主要有四种:

      Serializable:串行化,一个事务一个事务的执行。
      Repeatable read:可重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响。
      Read committed:读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值。
      Read uncommitted:读取未提交,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。
    

MySQL数据库默认使用可重复读( Repeatable read)。
使用乐观锁的时候,如果一个事务修改了库存并提交了事务,那其他的事务应该可以读取到修改后的数据值,所以不能使用可重复读的隔离级别,应该修改为读取已提交(Read committed)。

修改方式:

windows:

  • 先停止MySQL的服务 net stop MySQL服务名
  • 在mysql的文件夹内修改my.ini文件 添加一行 修改MySQL的隔离级别
transaction-isolation=READ-COMMITTED
  • 开启MySQL服务 net start MySQL服务名

在这里插入图片描述

Linux:

# 修改cnf文件
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 添加一行
transaction-isolation=READ-COMMITTED
# 修改完成之后重启
sudo service mysql restart
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值