完整电商项目--(八)商品订单模块(2): mysql乐观锁与 事务隔离级别

什么是并发?

前情提要:

  • 上节事务解决的问题是,多个逻辑任务之间是 必须同时成功,或者同时失败的(比如创建订单对象与创建订单商品对象)

并发场景

  • 当库存有限,但是让同事有多个用户购买,会发生并发问题。
  • 计入商品a有 1件商品, a,b用户同时购买,读取数据库中的商品数量时都是1 ! ,结果二人都创建订单成功,但是:数据库中很明显只有一件商品,不够卖给两个人。这就是接着要解决的并发问题。

解决并发的方式:

(1) 排队

  • 本质上还是一个一个的处理请求,但是不用让用户去感受到他在等别人执行完之后,才去执行他的,需要使用到 celery。 开启只有一个进程的celery,一个订单一个订单请求的在后台排队处理。

(2) 利用 **‘锁’**的方案。

悲观锁

  • 解释:这个锁是真实存在的,在mysql数据库当中,为指定的那个数据加,学过操作系统的话知道,相当于是一种临界资源。类似于互斥锁, 但是弊端就是处理不好很容易产生死锁
    在这里插入图片描述
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)

mysql事务隔离级别的问题

  • 问题引入: 上面的乐观锁已经可以很好地处理我们的业务需要了,但是有一点,他是放在一个事务里面MySQL默认的 事务隔离级别是 可重复读,也就是说,不管其他用户如何购买商品,影响不到 我们这个事务中对数据的判断,因为事务读取数据不会 更新,加入别人事务完成买走了商品还有0件,我们乐观锁读取到的还是之前的1,不会去 刷新 读取到新数据。
  • 解决: 更改数据库的 事务级别。
mysql事务级别
Serializable:
  • 串行化,一个事务一个事务的执行
  • 执行效率太低了,意思就是只允许一个事务执行,执行完毕才允许其他事务执行。
Repeatable read(默认):
  • 可重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响
Read committed:
  • 读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值
Read uncommitted:
  • 读取未提交,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值

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

修改事务级别方式(ubuntu):

在这里插入图片描述
在这里插入图片描述

现在是时候上我们的处理业务代码了

with transaction.atomic():   # 禁止事务自动提交
            # 开启事务
            sid = transaction.savepoint()
            # 2.创建订单基本对象
            order_id = '%s%09d' % (now.strftime('%Y%m%d%H%M%S'), user.id)
            total_count = 0
            total_amount = 0
            if pay_method == '1':
                # 待发货
                status = 1
            else:
                # 待支付
                status = 2
            order = OrderInfo.objects.create(
                order_id=order_id,
                user_id=user.id,
                address_id=address_id,
                total_count=0,
                total_amount=0,
                freight=10,
                pay_method=pay_method,
                status=status
            )

            # 3.查询商品对象
            skus = SKU.objects.filter(pk__in=cart_selected_int)

            # 4.遍历
            for sku in skus:
                cart_count = cart_dict_int.get(sku.id)
                # 4.1判断库存,不足则提示,如果足够则继续执行
                if sku.stock < cart_count:
                    # 回滚事务
                    transaction.savepoint_rollback(sid)
                    return http.JsonResponse({
    
    'code': RETCODE.PARAMERR, 'errmsg': '商品[%d]库存不足' % sku.id})

                # 4.2修改sku的库存、销量
                # sku.stock -= cart_count
                # sku.sales += cart_count
                # sku.save()

                # 4.2 使用乐观锁
                stock_old = sku.stock
                stock_new = sku.stock - cart_count
                sales_new = sku.sales + cart_count
                result = SKU.objects.filter(pk=sku.id, stock=stock_old).update(stock=stock_new, sales=sales_new)
                # result表示sql语句修改数据的个数
                if result == 0:
                    # 库存发生变化,未成功购买
                    transaction.savepoint_rollback(sid)
                    return http.JsonResponse({
    
    'code': RETCODE.PARAMERR, 'errmsg': '服务器忙,请稍候重试'})

                # 4.3创建订单商品对象
                order_sku = OrderGoods.objects.create(
                    order_id=order_id,
                    sku_id=sku.id,
                    count=cart_count,
                    price=sku.price
                )

                # 4.4 计算总金额、总数量
                total_count += cart_count
                total_amount += sku.price * cart_count

            # 5.修改订单对象的总金额、总数量
            order.total_count = total_count
            order.total_amount = total_amount + 10
            order.save()
            transaction.savepoint_commit(sid)
        # 6.删除购物车中选中的商品
        redis_cli.hdel('cart%d' % request.user.id, *cart_selected_int)
        redis_cli.srem('selected%d' % request.user.id, *cart_selected_int)

好的,基本上就处理完成了!

猜你喜欢

转载自blog.csdn.net/pythonstrat/article/details/108208527
今日推荐