python并发处理同一个文件_python数据库并发处理(乐观锁)

1.数据库并发处理问题

在多个用户同时发起对同一个数据提交修改操作时(先查询,再修改),会出现资源竞争的问题,导致最终修改的数据结果出现异常。

比如限量商品在热销时,当多个用户同时请求购买商品时,最终修改的数据就会出现异常

70

下面我们来写点代码还原一下现象:

1.新建项目Optimistic locking,创建应用app01,编辑models创建一张表并执行数据库迁移,如下:

from django.db importmodelsclassGoodsInfo(models.Model):"""商品"""name= models.CharField(max_length=50, verbose_name='名称')

stock= models.IntegerField(default=0, verbose_name='库存')classMeta:

db_table= 'tb_goodsinfo'

2.往数据库中插入一条数据:insert into tb_goodsinfo values(0, "macbook", 10);

3.定义Goods视图类,

增加判断库存和修改库存之间的间隙,就可以模拟出A用户尚未修改库存之前,B用户已经开始进行判断库存,导致误差:

from django.http importHttpResponsefrom rest_framework.generics importGenericAPIViewfrom app01.models importGoodsInfoclassGoods(GenericAPIView):"""购买商品"""

defpost(self, request):#获取请求头中查询字符串数据

goods_id = request.GET.get('goods_id')

count= int(request.GET.get('count'))#查询商品对象

goods = GoodsInfo.objects.filter(id=goods_id).first()#获取原始库存

origin_stock =goods.stock#判断商品库存是否充足

if origin_stock

importtime

time.sleep(5)#减少商品的库存数量,保存到数据库

goods.stock = origin_stock -count

goods.save()return HttpResponse(content="操作成功", status=200)

我们先使用postman来模拟单个用户请求

70

再来查询数据库,单个用户请求正常,(将stock恢复到10)

70

模拟多个用户请求

我们来使用两个postman模拟A,B用户同时请求,用户A买6套商品,用户B买5套商品

运行结果:

输出日志:

70

查询数据库:

70

两个postman发出的post请求均提示 “操作成功”

分析及结论:

当A用户请求的时候,goods.stock = origin_stock - count

A操作的结果:goods.stock = 10 - 6 = 4

可是B用户判断库存的时候,A还未将修改的数据保存到数据库,所以B获取的库存数量也是 10

B操作的结果:goods.stock = 10 - 5 = 5

写入数据库操作中,B的数据将A的数据覆盖,故最后的库存还是 5

2.解决办法:

如果使用给数据库加锁的方式,在给处理多个商品时可能会出现死锁,所以使用数据库中的乐观锁方式来处理效果较好

数据库乐观锁:

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

使用原生的SQL语句

update tb_goodsinfo set stock=5 where id=1 and stock=10;

使用Django中的语法

GoodsInfo.objects.filter(id=1, stock=10).update(stock=5)#GoodsInfo:模型类, id:商品id, stock:库存

改写视图类:

from django.http importHttpResponsefrom rest_framework.generics importGenericAPIViewfrom app01.models importGoodsInfoclassGoods(GenericAPIView):"""购买商品"""

defpost(self, request):#获取请求头中查询字符串数据

goods_id = request.GET.get('goods_id')

count= int(request.GET.get('count'))whileTrue:#查询商品对象

goods = GoodsInfo.objects.filter(id=goods_id).first()#获取原始库存

origin_stock =goods.stock#判断商品库存是否充足

if origin_stock

importtime

time.sleep(5)#减少商品的库存数量,保存到数据库

#goods.stock = origin_stock - count

#goods.save()

"""使用乐观锁进行处理,一步完成数据库的查询和更新"""

#update返回受影响的行数

result = GoodsInfo.objects.filter(id=goods.id, stock=origin_stock).update(stock=origin_stock -count)if result ==0:#表示更新失败,有人抢先购买了商品,重新获取库存信息,判断库存

continue

#表示购买成功,退出 while 循环

break

return HttpResponse(content="操作成功", status=200)

结果:

输出日志:

70

查询数据库

70

A用户返回 “操作成功”, B用户返回 “商品库存不足”

3.需要修改MySQL的事务隔离级别

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

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

Serializable 串行化,一个事务一个事务的执行

Repeatable read 可重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响

Read committed 读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值

Read uncommitted 读取为提交,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。

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

修改方法:

打开配置文件

70

修改隔离级别

70

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值