冲突比较少的时候,使用乐观锁。

冲突比较多的时候,使用悲观锁。

(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-订单并发处理--悲观锁和乐观锁的更多相关文章

  1. MySQL学习笔记(四)悲观锁与乐观锁

    恼骚 最近在搞并发的问题,订单的异步通知和主动查询会存在并发的问题,用到了Mysql数据库的 for update 锁 在TP5直接通过lock(true),用于数据库的锁机制 Db::name('p ...

  2. mysql悲观锁与乐观锁

    简介 数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性. 用途 乐观锁和悲观锁是并发控制主要采用的技术手段.无论是悲观 ...

  3. sql server 悲观锁和乐观锁的作用

    sql server对并发的处理-乐观锁和悲观锁 假如两个线程同时修改数据库同一条记录,就会导致后一条记录覆盖前一条,从而引发一些问题. 例如: 一个售票系统有一个余票数,客户端每调用一次出票方法,余 ...

  4. mysql-mysql悲观锁和乐观锁

    1.mysql的四种事务隔离级别 I. 对于同时运行多个事务,当这些事务访问数据库中的相同数据时,如果没有采取必要的隔离机制,就会导致各种并发问题. (1)脏读: 对于两个事物 T1, T2, T1 ...

  5. Hibernate解决高并发问题之:悲观锁 VS 乐观锁

    高并发问题是程序设计所必须要解决的问题,解决此类问题最主要的途径就是对对程序进行加锁控制.hibernate对加锁机制同样做出了实现,常用加锁方式为悲观锁和乐观锁.悲观锁指的是对数据被外界(包括本系统 ...

  6. mysql的锁--行锁,表锁,乐观锁,悲观锁

    一 引言--为什么mysql提供了锁 最近看到了mysql有行锁和表锁两个概念,越想越疑惑.为什么mysql要提供锁机制,而且这种机制不是一个摆设,还有很多人在用.在现代数据库里几乎有事务机制,aci ...

  7. Oracle数据库悲观锁与乐观锁详解

    数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住.而乐 ...

  8. 025 hibernate悲观锁、乐观锁

    Hibernate谈到悲观锁.乐观锁,就要谈到数据库的并发问题,数据库的隔离级别越高它的并发性就越差 并发性:当前系统进行了序列化后,当前读取数据后,别人查询不了,看不了.称为并发性不好 数据库隔离级 ...

  9. Mysql锁机制--乐观锁 & 悲观锁

    Mysql 系列文章主页 =============== 从 这篇 文章中,我们知道 Mysql 并发事务会引起更新丢失问题,解决办法是锁.所以本文将对锁(乐观锁.悲观锁)进行分析. 第一部分 悲观锁 ...

  10. Mysql共享锁、排他锁、悲观锁、乐观锁及其使用场景

    一.相关名词 |--表级锁(锁定整个表) |--页级锁(锁定一页) |--行级锁(锁定一行) |--共享锁(S锁,MyISAM 叫做读锁) |--排他锁(X锁,MyISAM 叫做写锁) |--悲观锁( ...

随机推荐

  1. 二叉搜索树中第K小的元素

    给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素. 说明:你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数. 示例 1: 输入: root = [ ...

  2. acwing 47. 二叉树中和为某一值的路径

    地址 https://www.acwing.com/problem/content/description/45/ 输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径. 从树的根结 ...

  3. <Graph> Topological + Undirected Graph 310 Union Find 261 + 323 + (hard)305

    310. Minimum Height Trees queue:  degree为1的顶点 degree[ i ] : 和 i 顶点关联的边数. 先添加整个图,然后BFS删除每一层degree为1的节 ...

  4. Repair Microsoft.VisualStudio.MinShell.Msi.Resources 2203 error And visual studio 2019 key

    1. Go to the properties of "My computer" 2. Go to advanced settings of the system 3. Go to ...

  5. something just like this---About Me

    endl:JX弱校oier,04年生,妹子,2019级高一新生,然后居然不知道该说什么了,尴尬 2019年3月开始接触oi,学的很慢(看起来脑子不太好用) 2019年7月创建了这个博客,在收到“恭喜! ...

  6. 持续集成(CI):API自动化+Jenkins定时构建

    一.系统管理 1.管理监控配置 系统管理>>系统设置>>管理监控配置 2.设置接收测试报告的邮箱 系统管理>>系统设置>>配置Extended E-ma ...

  7. web自动化针对PO模式进行二次封装之basepage

    在PO模式当中,我们做到了页面对象与测试用例的分离,但在页面对象编写时,我们仍然还有优化的空间.页面对象有一些共同的基本操作,可以封装起来,并可以在基本操作当中加上日志和异常截图的处理.比如说我们在查 ...

  8. 转载:ubuntu下编译安装nginx及注册服务

    原文地址:https://www.cnblogs.com/EasonJim/p/7806879.html 安装gcc g++的依赖库 sudo apt-get install build-essent ...

  9. Linux 内存释放

    简介 linux 内存释放通过如下命令,将cache与buff根据环境进行释放操作,避免重启释放内存. 操作 1.将内存中buff数据保存磁盘 sync 2.清理cache与buff缓存 echo 3 ...

  10. mysqlslap详解--MySQL自带的性能压力测试工具(转)

    本文的参考博客地址为:https://blog.csdn.net/fuzhongfaya/article/details/80943991 和 https://www.cnblogs.com/davy ...