zoukankan      html  css  js  c++  java
  • 使用乐观锁实现电商下单问题?

    一. 乐观锁

    乐观锁并不是真实存在的锁,而是在更新的时候判断此时的库存是否是之前查询出的库存,如果相同,表示没人修改,可以更新库存,否则表示别人抢过资源,不再执行库存更新。类似如下操作。

    # sql实现语句
    update tb_apple set stock=2 where id=1 and stock=7;
    # djano中ORM实现语句 apple.objects.filter(id
    =1, stock=7).update(stock=2)

    二.乐观锁下单逻辑

    第一步 : 下单逻辑

    def create(self, validated_data):
            """
            保存订单
            """
            # 获取当前下单用户
            user = self.context['request'].user
    
            # 组织订单编号 20170903153611+user.id
            # timezone.now() -> datetime
            order_id = timezone.now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
    
            address = validated_data['address']
            pay_method = validated_data['pay_method']
    
            # 生成订单
            with transaction.atomic():
                # 创建一个保存点
                save_id = transaction.savepoint()
    
                try:
                    # 创建订单信息
                    order = OrderInfo.objects.create(
                        order_id=order_id,
                        user=user,
                        address=address,
                        total_count=0,
                        total_amount=Decimal(0),
                        freight=Decimal(10),
                        pay_method=pay_method,
                        status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH'] else OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                    )
                    # 获取购物车信息
                    redis_conn = get_redis_connection("cart")
                    redis_cart = redis_conn.hgetall("cart_%s" % user.id)
                    cart_selected = redis_conn.smembers('cart_selected_%s' % user.id)
    
                    # 将bytes类型转换为int类型
                    cart = {}
                    for sku_id in cart_selected:
                        cart[int(sku_id)] = int(redis_cart[sku_id])
    
                    # # 一次查询出所有商品数据
                    # skus = SKU.objects.filter(id__in=cart.keys())
    
                    # 处理订单商品
                    sku_id_list = cart.keys()
                    for sku_id in sku_id_list:
                        while True:
                            sku = SKU.objects.get(id=sku_id)
    
                            sku_count = cart[sku.id]
    
                            # 判断库存
                            origin_stock = sku.stock  # 原始库存
                            origin_sales = sku.sales  # 原始销量
    
                            if sku_count > origin_stock:
                                transaction.savepoint_rollback(save_id)
                                raise serializers.ValidationError('商品库存不足')
    
                            # 用于演示并发下单
                            # import time
                            # time.sleep(5)
    
                            # 减少库存
                            # sku.stock -= sku_count
                            # sku.sales += sku_count
                            # sku.save()
                            new_stock = origin_stock - sku_count
                            new_sales = origin_sales + sku_count
    
                            # 根据原始库存条件更新,返回更新的条目数,乐观锁
                            ret = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
                            if ret == 0:
                                continue
    
                            # 累计商品的SPU 销量信息
                            sku.goods.sales += sku_count
                            sku.goods.save()
    
                            # 累计订单基本信息的数据
                            order.total_count += sku_count  # 累计总金额
                            order.total_amount += (sku.price * sku_count)  # 累计总额
    
                            # 保存订单商品
                            OrderGoods.objects.create(
                                order=order,
                                sku=sku,
                                count=sku_count,
                                price=sku.price,
                            )
    
                            # 更新成功
                            break
    
                    # 更新订单的金额数量信息
                    order.total_amount += order.freight
                    order.save()
    
                except serializers.ValidationError:
                    raise
                except Exception as e:
                    logger.error(e)
                    transaction.savepoint_rollback(save_id)
                    raise
    
                # 更新redis中保存的购物车数据
                pl = redis_conn.pipeline()
                pl.hdel('cart_%s' % user.id, *cart_selected)
                pl.srem('cart_selected_%s' % user.id, *cart_selected)
                pl.execute()
                return order

    第二步 : 修改数据库事务隔离级别

    事务隔离级别指的是在处理同一个数据的多个事务中,一个事务修改数据后,其他事务何时能看到修改后的结果。
    
    MySQL数据库事务隔离级别主要有四种:
    
    Serializable 串行化,一个事务一个事务的执行
    Repeatable read 可重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响
    Read committed 读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值
    Read uncommitted 读取为提交,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。
    MySQL数据库默认使用可重复读( Repeatable read),而使用乐观锁的时候,如果一个事务修改了库存并提交了事务,那其他的事务应该可以读取到修改后的数据值,所以不能使用可重复读的隔离级别,应该修改为读取已提交Read committed。

    # 修改事务隔离级别,以mysql为例,在数据库的 mysqld.cnf:
    sudo vim mysqld.cnf
    # 修改:
    transaction-isolation=READ-COMMITTED


  • 相关阅读:
    codeforces 820 D. Mister B and PR Shifts(思维)
    codeforces 820 C. Mister B and Boring Game(找规律)
    玲珑杯 1137
    codeforces 817 D. Imbalanced Array(单调栈+思维)
    Atcoder D
    Atcoder C
    Atcode B
    codeforces 816 E. Karen and Supermarket(树形dp)
    codeforces 816 D. Karen and Test(逆元+思维+组合数)
    codeforces 816 C. Karen and Game(模拟+思维)
  • 原文地址:https://www.cnblogs.com/zhaijihai/p/11928240.html
Copyright © 2011-2022 走看看