django项目中在创建订单时如何使用事务和锁(悲观锁乐观锁)

    科技2022-08-17  109

    事务的四大特性:

    原子性、一致性、隔离性、持久性(ACID)

    django中操作数据库如果想使用事务,需要导入一个模块

    from django.db import transaction

    使用transaction.atomic装饰器对函数进行装饰,该函数中所有对数据库的操作就会形成一个事务,要么都执行,要么都不执行

    同时,还可以在代码中设置保存点!

    事务+悲观锁:

    (1)悲观锁:查询的时候加锁,事务结束时才会释放锁

    事务+悲观锁代码如下:

    # 前端传递的参数:地址id:addr_id,支付方式:pay_method,购买的商品id字符串:sku_ids 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") # 校验数据 if not all([addr_id, pay_method, sku_ids]): return JsonResponse({"res": 1, "errmsg": "参数不完整"}) # 校验支付方式 if pay_method not in OrderInfo.PAY_METHODS.keys(): return JsonResponse({"res": 2, "errmsg": "非法的支付方式"}) # 校验地址 try: addr = Address.objects.get(id=addr_id) except Address.DoesNotExist: return JsonResponse({"res": 3, "errmsg": "地址非法"}) # todo: 创建订单核心业务 # 组织参数 # 订单id:20201005143016+用户id order_id = datetime.now().strftime("%Y%m%d%H%M%S") + str(user.id) # 总数目和总金额 total_count = 0 total_price = 0 # 运费 transit_price = 10 # 设置事务保存点 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_{}".format(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 GoodsSKU.DoesNotExist: # 商品不存在 transaction.savepoint_rollback(save_id) # 回滚到事务保存点 return JsonResponse({"res": 4, "errmsg": "商品不存在"}) # 测试悲观锁的代码 # print("user:{},stock:{}".format(user.id, sku.stock)) # import time # time.sleep(10) # 从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) # todo: 更新商品的库存和销量 sku.stock -= int(count) sku.sales += int(count) sku.save() # todo: 累加计算订单商品的总数量和总价格 total_count += int(count) amount = sku.price * int(count) total_price += amount # todo: 更新订单信息表中的商品总数量和总价格 order.total_count = total_count order.total_price = total_price # todo: 根据订单总价格更新运费 if total_price >= 80: order.transit_price = 0 order.save() except Exception as err: transaction.savepoint_rollback(save_id) # 回滚到事务保存点 return JsonResponse({"res": 7, "errmsg": "下单失败"}) # 提交事务 transaction.savepoint_commit(save_id) # todo: 清除用户购物车中对应的记录 conn.hdel(cart_key, *sku_ids) # 返回Json数据 return JsonResponse({"res": 5, "message": "创建成功"})

    事务+乐观锁+修改mysql事务隔离级别为:可读取提交内容

    (1)乐观锁:查询的时候不加锁,但是在更新的时候做一个判断,如果跟之前库存一样才更新,不一样不更新,但是有的时候库存量还够,只是被修改了,所以为了避免这种情况,我们需要对遍历3次,如果3次都不一样,直接回滚事务

    (2)设置Mysql事务的隔离级别

    事务中使用乐观锁如何解决幻读:修改Mysql配置文件/etc/mysql/mysql.conf.d/mysqld.cnf

    增加下面1标注的这句代码:意思是可读取提交的内容

    (4)重启mysql服务:sudo service mysql restart

    # 前端传递的参数:地址id:addr_id,支付方式:pay_method,购买的商品id字符串:sku_ids # 事务+乐观锁的使用 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") # 校验数据 if not all([addr_id, pay_method, sku_ids]): return JsonResponse({"res": 1, "errmsg": "数据不完整"}) # 校验支付方式 if pay_method not in OrderInfo.PAY_METHODS.keys(): return JsonResponse({"res": 2, "errmsg": "非法的支付方式"}) # 校验地址 try: addr = Address.objects.get(id=addr_id) except Address.DoesNotExist: return JsonResponse({"res": 3, "errmsg": "地址非法"}) # todo: 创建订单核心业务 # 组织参数 # 订单id:20201005143016+用户id order_id = datetime.now().strftime("%Y%m%d%H%M%S") + str(user.id) # 总数目和总金额 total_count = 0 total_price = 0 # 运费 transit_price = 10 # 设置事务保存点 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_{}".format(user.id) sku_ids = sku_ids.split(",") for sku_id in sku_ids: for i in range(3): # 获取商品的信息 try: sku = GoodsSKU.objects.get(id=sku_id) except GoodsSKU.DoesNotExist: # 商品不存在 transaction.savepoint_rollback(save_id) # 回滚到事务保存点 return JsonResponse({"res": 4, "errmsg": "商品不存在"}) # 从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 orgin_sales = sku.sales new_stock = orgin_stock - int(count) new_sales = orgin_sales + int(count) # 测试乐观锁的代码 # print("user:{},times:{},stock:{}".format(user.id, i, sku.stock)) # import time # time.sleep(10) # update df_goods_sku set stock=new_stock, sales=new_sales # where id = sku_id and stock = orgin_stock; # filter返回的查询集可以使用update方法,返回的是受影响的行数 res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock) \ .update(stock=new_stock, sales=new_sales) if res == 0: if i == 2: # 尝试的第三次 transaction.savepoint_rollback(save_id) # 回滚事务 return JsonResponse({"res": 7, "errmsg": "下单失败2"}) continue # todo: 向 df_order_goods表中插入一条记录 OrderGoods.objects.create(order=order, sku=sku, count=count, price=sku.price) # todo: 累加计算订单商品的总数量和总价格 total_count += int(count) amount = sku.price * int(count) total_price += amount # 一次就更新成功,跳出循环 break # todo: 更新订单信息表中的商品总数量和总价格 order.total_count = total_count order.total_price = total_price # todo: 根据订单总价格更新运费 if total_price >= 80: order.transit_price = 0 order.save() except Exception as err: transaction.savepoint_rollback(save_id) # 回滚到事务保存点 return JsonResponse({"res": 7, "errmsg": "下单失败"}) # 提交事务 transaction.savepoint_commit(save_id) # todo: 清除用户购物车中对应的记录 conn.hdel(cart_key, *sku_ids) # 返回Json数据 return JsonResponse({"res": 5, "message": "创建成功"})

    总结:在冲突比较少的时候,使用乐观锁,因为乐观锁省去了加锁释放锁的开销,提高性能

    冲突比较多的时候,使用悲观锁,减少遍历次数,乐观锁重复代价比较大的时候使用悲观锁!

    Processed: 0.019, SQL: 9