商品超卖问题:多个用户同时下单同一个商品时,可能会出现资源竞争问题,导致库存结果出现异常
乐观锁解决:
乐观锁并不是真正的锁,只是更新数据的时候多加一层判断
更新的时候判断此时库存是否和之前查询的库存一样,如果一样则表示没人修改,可以进行更新;否则表示有人抢过该资源,不再进行更新。类似下面操作:
update tb_sku set stock=2 where id=1 and stock=7;
SKU.objects.filter(id=1, stock=7).update(stock=2)
思路:
创建保存点
查询库存
更新库存时,将之前查询的库存和商品id一起作为更新的条件
当受影响行为0表示更新失败,回滚到保存点(没有操作数据库的时候)
重新查询,重复到n次不成功返回错误 (这里while True 直到库存为0停止)
示例:
from django.db import transaction
# 部分代码使用事物
with transaction.atomic():
# 创建保存点
save_id = transaction.savepoint()
try:
# 生成订单信息
order = OrderInfo.objects.create(
...
)
# 从redis取购物车要结算的商品数据(省略写法)
...
cart = {shu.id: sku_count, ...}
while True: # 直到没有库存为止
sku = SKU.objects.get(pk=sku_id) # 不加锁查询
# 购物车商品数量
count = cart[shu.id]
# 获取原始库存
origin_stock = sku.stock
# 判断商品库存是否充足
if origin_stock < count:
# 库存不足
raise serializers.ValidationError('库存不足')
# 演示并发请求
import time
time.sleep(5)
# 记录原来的值
origin_sales = sku.sales
# 计算要更新的值
new_stock = origin_stock - count
new_sales = origin_sales + count
# 返回受影响的行数
ret = SKU.objects.filter(id=sku.id,stock=origin_stock).update(stock=new_stock,sales=new_sales)
if ret == 0:
# 更新成功,进入下次循环重新判断
continue
else:
# 保存订单商品数据
...
# 更新成功,退出while循环
break
...
except Exception as e:
# 提交保存点
transaction.savepoint_rollback(save_id)
raise serializers.ValidationError('下单失败')
# 提交事务
transaction.savepoint_commit(save_id)
# 清除购物车中已经结算的商品
...
return order