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之数据库并发处理相关推荐

  1. 创建Django项目和模型(创建工程、子应用、设置pycharm环境、使用Django进行数据库开发的步骤)

    1.创建Django项目 文档:Writing your first Django app, part 1 | Django documentation | Django 步骤 创建Django项目 ...

  2. python3 django配置数据库(mysql)

    python3 django配置数据库(mysql)http://www.bieryun.com/3311.html python3 下的mysql驱动 django 连接mysql默认驱动是MySQ ...

  3. django oracle数据库配置,django连接oracle时setting 配置方法

    下一步是将新创建的应用程序与项目相关联.为此,您需要编辑 myproj 文件夹中的 settings.py 文件,将字符串"myproj.myapp"追加到 INSTALLED_A ...

  4. Django与数据库操作

    Django与数据库操作 数据库连接的方法 web 框架 django --- 自己内部实现 (ORM) + pymysql(连接)Flask,tornado ---pymysqlSQLArchemy ...

  5. 使用south实现Django的数据库升级迁移

    Technorati 标签: django,south,数据库迁移 Ruby有牛哄哄的Rails Migration实现数据的升级和迁移,django呢? 有south. 已有的应用要支持south( ...

  6. django模型——数据库(二)

    模型--数据库(二) 实验简介 模型的一些基本操作,save方法用于把对象写入到数据库,objects是模型的管理器,可以使用它的delete.filter.all.order_by和update等函 ...

  7. django mysql数据同步_[django同步数据库]Django去操作已经存在数据的数据库

    数据库,各种表结构已经创建好了,甚至连数据都有了,此时,我要用Django管理这个数据库,ORM映射怎么办??? Django是最适合所谓的green-field开发,即从头开始一个新的项目 但是呢, ...

  8. Django——创建数据库和表

    Django--创建数据库和表 Django拥有内置的ORM框架(object relational mapping),通过对象操作数据库. 模型是项目的数据来源,其中每一个模型都是一个python类 ...

  9. [Django ]Django 的数据库操作

    Django 的数据库操作 有之前的基础,那么我们就可以开始对数据库进行操作. 一.数据库配置 配置 MySql 在主目录的 settings.py 中修改 Python 1 2 3 4 5 6 7 ...

最新文章

  1. angular4 note
  2. JavaScript里的循环方法:forEach,for-in,for-of
  3. XidianOJ 1176 ship
  4. 零基础入门 HTML 的 8 分钟极简教程
  5. 如何给PDF文件交换页面?操作方法你知道吗?
  6. LightSwitch社区资源搜集
  7. mysql操作语句(简单笔记)
  8. 二层协议--LACP协议总结
  9. 优秀的程序员是没有性生活的
  10. 复杂性思维中文第二版 八、自组织临界
  11. 【2021 年终总结】一年涨粉100倍,有规划始执行~成功一半
  12. dom4j解析XML入门指北
  13. 移动直播之网红主播怎样将直播内容推到斗鱼直播平台的方案
  14. 函数模板和普通函数区别
  15. 四川乐而得教育:拼多多店铺推广要注意哪些优化
  16. DEEPIN系统下安装wine
  17. Karamata 不等式
  18. 计算机微格教学心得体会,微格教学心得体会6篇_微格教学体会报告
  19. Django 做个小后台,细节在完善一点点,滚雪球学 Python 第三阶段
  20. 微信小程序——点击事件传参没有数据(undefined)

热门文章

  1. 递归问题之小白上楼梯
  2. 利用OpenSSL,用国密SM4算法来给文件加密、解密
  3. 51 单片机 程序 浇花器 含水率 proteus 仿真 实物
  4. 倾角传感器的无线传输有几种?
  5. 软件工程如何改变世界
  6. 【JavaSE】Java中的异常那些事儿
  7. 【skynet】skynet入口解析
  8. 硕士毕业论文——文献阅读
  9. springboot整合mybatis bean注入失败
  10. (10)3DMAX之常用工具(镜像、对齐、组、层资源管理器、冻结、软件专家模式、物体半透明显示、变换虚拟体显示切换、自适应降级)