django-订单并发处理--悲观锁和乐观锁
冲突比较少的时候,使用乐观锁。
冲突比较多的时候,使用悲观锁。
(1) 悲观锁
select * from df_goods_sku where id=17 for update;

悲观锁获取数据时对数据行了锁定,其他事务要想获取锁,必须等原事务结束。
视图函数views.py
from django.db import transaction # 事务处理
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') # 1,3 # 校验参数
if not all([addr_id, pay_method, sku_ids]):
return JsonResponse({'res':1, 'errmsg':'参数不完整'}) # 校验支付方式
if pay_method not in OrderInfo.PAY_METHODS.keys(): # 需要在orders/model.py中添加PAY_METHODS字典
return JsonResponse({'res':2, 'errmsg':'非法的支付方式'}) # 校验地址
try:
addr = Address.objects.get(id=addr_id)
except Address.DoesNotExist:
# 地址不存在
return JsonResponse({'res':3, 'errmsg':'地址非法'}) # todo: 创建订单核心业务 # 组织参数
# 订单id: 20171122181630+用户id
order_id = datetime.now().strftime('%Y%m%d%H%M%S')+str(user.id) # 运费
transit_price = 10 # 总数目和总金额
total_count = 0
total_price = 0 # 设置保存点
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_%d'%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:
# 商品不存在
transaction.savepoint_rollback(save_id) # 回滚到保存点
return JsonResponse({'res':4, 'errmsg':'商品不存在'}) # 查看谁拿到锁
print('user:%d stock:%d'%(user.id,sku.stock)) # 从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) import time
time.sleep(10)
# todo: 更新商品的库存和销量
sku.stock -= int(count)
sku.sales += int(count)
sku.save() # todo: 累加计算订单商品的总数量和总价格
amount = sku.price*int(count)
total_count += int(count)
total_price += amount # todo: 更新订单信息表中的商品的总数量和总价格
order.total_count = total_count
order.total_price = total_price
order.save()
except Exception as e:
transaction.savepoint_rollback(save_id) # 回滚到保存点
return JsonResponse({'res': 7, 'errmsg': '下单失败'}) # 如果没失败则提交事务
transaction.savepoint_commit(save_id)
# todo: 清除用户购物车中对应的记录
conn.hdel(cart_key, *sku_ids) # 返回应答
return JsonResponse({'res':5, 'message':'创建成功'})
(2) 乐观锁
查询时不锁数据,提交更改时进行判断.
update df_goods_sku set stock=0, sales=1 where id=17 and stock=1;
使用乐观锁前,要先 设置mysql事务的隔离级别
打开mysql配置文件: sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
添加下面一行代码
transaction-isolation = READ-COMMITTED

保存配置文件,重启mysql服务。
sudo service mysql restart
实例views.py
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') # 1,3 # 校验参数
if not all([addr_id, pay_method, sku_ids]):
return JsonResponse({'res':1, 'errmsg':'参数不完整'}) # 校验支付方式
if pay_method not in OrderInfo.PAY_METHODS.keys(): # 需要在orders/model.py中添加PAY_METHODS字典
return JsonResponse({'res':2, 'errmsg':'非法的支付方式'}) # 校验地址
try:
addr = Address.objects.get(id=addr_id)
except Address.DoesNotExist:
# 地址不存在
return JsonResponse({'res':3, 'errmsg':'地址非法'}) # todo: 创建订单核心业务 # 组织参数
# 订单id: 20171122181630+用户id
order_id = datetime.now().strftime('%Y%m%d%H%M%S')+str(user.id) # 运费
transit_price = 10 # 总数目和总金额
total_count = 0
total_price = 0 # 设置保存点
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_%d'%user.id sku_ids = sku_ids.split(',')
for sku_id in sku_ids:
for i in range(3): # 多次尝试
# 获取商品的信息
try:
sku = GoodsSKU.objects.select_for_update().get(id=sku_id)
except:
# 商品不存在
transaction.savepoint_rollback(save_id) # 回滚到保存点
return JsonResponse({'res':4, 'errmsg':'商品不存在'}) # 查看谁拿到锁
print('user:%d stock:%d'%(user.id,sku.stock)) # 从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 # 原有的库存
new_stock = orgin_stock - int(count) # 要修改的库存
new_sales = sku.sales + int(count) # 要修改的销量 # 返回受影响行数 查询是否成功 放在添加记录前先检测再添加 --乐观锁
res = GoodsSKU.objects.filter(id=sku_id, stock=orgin_stock).update(stock=new_stock, sales=new_sales)
if res == 0:
if i == 2:
# 尝试的第3次,失败了回滚
transaction.savepoint_rollback(save_id)
return JsonResponse({'res': 7, 'errmsg': '下单失败'})
continue # todo: 向df_order_goods表中添加一条记录
OrderGoods.objects.create(order=order,
sku=sku,
count=count,
price=sku.price) # todo: 累加计算订单商品的总数量和总价格
amount = sku.price*int(count)
total_count += int(count)
total_price += amount # 跳出循环
break # todo: 更新订单信息表中的商品的总数量和总价格
order.total_count = total_count
order.total_price = total_price
order.save()
except Exception as e:
transaction.savepoint_rollback(save_id) # 回滚到保存点
return JsonResponse({'res': 7, 'errmsg': '下单失败7'}) # 如果没失败则提交事务
transaction.savepoint_commit(save_id)
# todo: 清除用户购物车中对应的记录
conn.hdel(cart_key, *sku_ids) # 返回应答
return JsonResponse({'res':5, 'message':'创建成功'})
django-订单并发处理--悲观锁和乐观锁的更多相关文章
- MySQL学习笔记(四)悲观锁与乐观锁
恼骚 最近在搞并发的问题,订单的异步通知和主动查询会存在并发的问题,用到了Mysql数据库的 for update 锁 在TP5直接通过lock(true),用于数据库的锁机制 Db::name('p ...
- mysql悲观锁与乐观锁
简介 数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性. 用途 乐观锁和悲观锁是并发控制主要采用的技术手段.无论是悲观 ...
- sql server 悲观锁和乐观锁的作用
sql server对并发的处理-乐观锁和悲观锁 假如两个线程同时修改数据库同一条记录,就会导致后一条记录覆盖前一条,从而引发一些问题. 例如: 一个售票系统有一个余票数,客户端每调用一次出票方法,余 ...
- mysql-mysql悲观锁和乐观锁
1.mysql的四种事务隔离级别 I. 对于同时运行多个事务,当这些事务访问数据库中的相同数据时,如果没有采取必要的隔离机制,就会导致各种并发问题. (1)脏读: 对于两个事物 T1, T2, T1 ...
- Hibernate解决高并发问题之:悲观锁 VS 乐观锁
高并发问题是程序设计所必须要解决的问题,解决此类问题最主要的途径就是对对程序进行加锁控制.hibernate对加锁机制同样做出了实现,常用加锁方式为悲观锁和乐观锁.悲观锁指的是对数据被外界(包括本系统 ...
- mysql的锁--行锁,表锁,乐观锁,悲观锁
一 引言--为什么mysql提供了锁 最近看到了mysql有行锁和表锁两个概念,越想越疑惑.为什么mysql要提供锁机制,而且这种机制不是一个摆设,还有很多人在用.在现代数据库里几乎有事务机制,aci ...
- Oracle数据库悲观锁与乐观锁详解
数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住.而乐 ...
- 025 hibernate悲观锁、乐观锁
Hibernate谈到悲观锁.乐观锁,就要谈到数据库的并发问题,数据库的隔离级别越高它的并发性就越差 并发性:当前系统进行了序列化后,当前读取数据后,别人查询不了,看不了.称为并发性不好 数据库隔离级 ...
- Mysql锁机制--乐观锁 & 悲观锁
Mysql 系列文章主页 =============== 从 这篇 文章中,我们知道 Mysql 并发事务会引起更新丢失问题,解决办法是锁.所以本文将对锁(乐观锁.悲观锁)进行分析. 第一部分 悲观锁 ...
- Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景
一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--悲观锁( ...
随机推荐
- 控制label标签的宽度,不让它换行 label标签左对齐
1==>控制label标签的宽度.解决字段名太长时,不会换行显示 label-width="100px" label标签的宽度是100px; style="widt ...
- 初学Python:Python的发展历史及下载安装
Python作为一种计算机程序设计语言,自20世纪90年代初诞生至如今被人们逐渐悉知,经过版本更新以及功能添加,已广泛应用于各种独立的.大型项目的开发.Python 已经成为最受欢迎的程序设计语言之一 ...
- CF1244C The Football Season
题目链接 problem 给定\(n,p,w,d\),求解任意一对\((x,y)\)满足\[xw+yd=p\\ x + y \le n\] \(1\le n\le 10^{12},0\le p\le ...
- 【洛谷5299】[PKUWC2018] Slay the Spire(组合数学)
点此看题面 大致题意: 有\(n\)张强化牌\(a_i\)和\(n\)张攻击牌\(b_i\),每张牌有一个权值(强化牌的权值大于\(1\)),每张强化牌能使所有攻击牌的权值乘上这张强化牌的权值,每张攻 ...
- 模块基础实战之ATM和购物车系统分文件处理
目录 一.项目地址 二.功能需求 一.项目地址 https://github.com/nickchen121/atm 二.功能需求 FUNC_MSG = { '0': '注销', '1': '登录', ...
- C#开发BIMFACE系列26 服务端API之获取模型数据11:获取单个面积分区信息
系列目录 [已更新最新开发文章,点击查看详细] 在<C#开发BIMFACE系列25 服务端API之获取模型数据9:获取楼层对应面积分区列表>一文中介绍了如何获取单个模型中单个楼层包 ...
- css 修改placeholder字体颜色字体大小 修改input记住账号密码后的默认背景色
壹 ❀ 引 本来这个阶段的项目页面都是给实习生妹子做的,我只用写写功能接接数据,但这两天妹子要忙翻译,这个工作阶段也快结束了导致有点慌,只能自己把剩余的几个小页面给写了. 那么做页面的过程中,UI也 ...
- 阿里小哥带你玩转JVM:揭秘try-catch-finally在JVM底层都干了些啥?
让我们准备一个函数: 然后,反编译他的字节码: 首先我们介绍异常表:在编译生成的字节码中,每个方法都附带一个异常表. 异常表中的每一个条目代表一个异常处理器,并且由 from 指针.to 指针 ...
- C#截图操作(几种截图方法)
公共函数获取屏幕截图private Bitmap GetScreenCapture(){ Rectangle tScreenRect = new Rectangle(0, 0, Screen.Prim ...
- flex布局和边框阴影
"妄"眼欲穿-CSS之flex布局和边框阴影 妄:狂妄: 不会的东西只有怀着一颗狂妄的心,假装能把它看穿吧. flex布局 main axis:主轴:cross axis:交叉轴 ...