Django之数据库并发处理
1.数据库并发处理问题
在多个用户同时发起对同一个数据提交修改操作时(先查询,再修改),会出现资源竞争的问题,导致最终修改的数据结果出现异常。
比如限量商品在热销时,当多个用户同时请求购买商品时,最终修改的数据就会出现异常
下面我们来写点代码还原一下现象:
1.新建项目Optimistic locking,创建应用app01,编辑models创建一张表并执行数据库迁移,如下:
from django.db import modelsclass GoodsInfo(models.Model):""" 商品 """name = models.CharField(max_length=50, verbose_name='名称')stock = models.IntegerField(default=0, verbose_name='库存')class Meta:db_table = 'tb_goodsinfo'
2.往数据库中插入一条数据:insert into tb_goodsinfo values(0, "macbook", 10);
3.定义Goods视图类,
- 增加判断库存和修改库存之间的间隙,就可以模拟出A用户尚未修改库存之前,B用户已经开始进行判断库存,导致误差:
from django.http import HttpResponse
from rest_framework.generics import GenericAPIView
from app01.models import GoodsInfoclass Goods(GenericAPIView):""" 购买商品 """def post(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 < count:return HttpResponse(content="商品库存不足", status=400)# 演示多个用户并发请求import timetime.sleep(5)# 减少商品的库存数量,保存到数据库goods.stock = origin_stock - countgoods.save()return HttpResponse(content="操作成功", status=200)
4.定义路由:
from django.conf.urls import url
from . import viewsurlpatterns =[url(r'^goods/$', views.Goods.as_view()),
]
我们先使用postman来模拟单个用户请求
- 再来查询数据库,单个用户请求正常,(将stock恢复到10)
模拟多个用户请求
我们来使用两个postman模拟A,B用户同时请求,用户A买6套商品,用户B买5套商品
运行结果:
- 输出日志:
- 查询数据库:
- 两个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 import HttpResponse
from rest_framework.generics import GenericAPIView
from app01.models import GoodsInfoclass Goods(GenericAPIView):""" 购买商品 """def post(self, request):# 获取请求头中查询字符串数据goods_id = request.GET.get('goods_id')count = int(request.GET.get('count'))while True:# 查询商品对象goods = GoodsInfo.objects.filter(id=goods_id).first()# 获取原始库存origin_stock = goods.stock# 判断商品库存是否充足if origin_stock < count:return HttpResponse(content="商品库存不足", status=400)# 演示并发请求import timetime.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 循环breakreturn HttpResponse(content="操作成功", status=200)
多用户请求结果:
- 输出日志:
- 查询数据库
- A用户返回 “操作成功”, B用户返回 “商品库存不足”
3.需要修改MySQL的事务隔离级别
事务隔离级别指的是在处理同一个数据的多个事务中,一个事务修改数据后,其他事务何时能看到修改后的结果。
MySQL数据库事务隔离级别主要有四种:
Serializable 串行化,一个事务一个事务的执行Repeatable read 可重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响Read committed 读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值Read uncommitted 读取为提交,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。
MySQL数据库默认使用可重复读( Repeatable read),而使用乐观锁的时候,如果一个事务修改了库存并提交了事务,那其他的事务应该可以读取到修改后的数据值,所以不能使用可重复读的隔离级别,应该修改为读取已提交Read committed。
修改方法:
打开配置文件
修改隔离级别
OK
Django之数据库并发处理相关推荐
- 创建Django项目和模型(创建工程、子应用、设置pycharm环境、使用Django进行数据库开发的步骤)
1.创建Django项目 文档:Writing your first Django app, part 1 | Django documentation | Django 步骤 创建Django项目 ...
- python3 django配置数据库(mysql)
python3 django配置数据库(mysql)http://www.bieryun.com/3311.html python3 下的mysql驱动 django 连接mysql默认驱动是MySQ ...
- django oracle数据库配置,django连接oracle时setting 配置方法
下一步是将新创建的应用程序与项目相关联.为此,您需要编辑 myproj 文件夹中的 settings.py 文件,将字符串"myproj.myapp"追加到 INSTALLED_A ...
- Django与数据库操作
Django与数据库操作 数据库连接的方法 web 框架 django --- 自己内部实现 (ORM) + pymysql(连接)Flask,tornado ---pymysqlSQLArchemy ...
- 使用south实现Django的数据库升级迁移
Technorati 标签: django,south,数据库迁移 Ruby有牛哄哄的Rails Migration实现数据的升级和迁移,django呢? 有south. 已有的应用要支持south( ...
- django模型——数据库(二)
模型--数据库(二) 实验简介 模型的一些基本操作,save方法用于把对象写入到数据库,objects是模型的管理器,可以使用它的delete.filter.all.order_by和update等函 ...
- django mysql数据同步_[django同步数据库]Django去操作已经存在数据的数据库
数据库,各种表结构已经创建好了,甚至连数据都有了,此时,我要用Django管理这个数据库,ORM映射怎么办??? Django是最适合所谓的green-field开发,即从头开始一个新的项目 但是呢, ...
- Django——创建数据库和表
Django--创建数据库和表 Django拥有内置的ORM框架(object relational mapping),通过对象操作数据库. 模型是项目的数据来源,其中每一个模型都是一个python类 ...
- [Django ]Django 的数据库操作
Django 的数据库操作 有之前的基础,那么我们就可以开始对数据库进行操作. 一.数据库配置 配置 MySql 在主目录的 settings.py 中修改 Python 1 2 3 4 5 6 7 ...
最新文章
- angular4 note
- JavaScript里的循环方法:forEach,for-in,for-of
- XidianOJ 1176 ship
- 零基础入门 HTML 的 8 分钟极简教程
- 如何给PDF文件交换页面?操作方法你知道吗?
- LightSwitch社区资源搜集
- mysql操作语句(简单笔记)
- 二层协议--LACP协议总结
- 优秀的程序员是没有性生活的
- 复杂性思维中文第二版 八、自组织临界
- 【2021 年终总结】一年涨粉100倍,有规划始执行~成功一半
- dom4j解析XML入门指北
- 移动直播之网红主播怎样将直播内容推到斗鱼直播平台的方案
- 函数模板和普通函数区别
- 四川乐而得教育:拼多多店铺推广要注意哪些优化
- DEEPIN系统下安装wine
- Karamata 不等式
- 计算机微格教学心得体会,微格教学心得体会6篇_微格教学体会报告
- Django 做个小后台,细节在完善一点点,滚雪球学 Python 第三阶段
- 微信小程序——点击事件传参没有数据(undefined)