django-订单并发处理--悲观锁和乐观锁
2024-08-28 15:14:19
冲突比较少的时候,使用乐观锁。
冲突比较多的时候,使用悲观锁。
(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':'创建成功'})
最新文章
- Oracle体系结构总结
- tomcat WEB-INF中的结构
- Spring+SpringMVC+MyBatis)
- linux命令(10)使用kill杀死含有指定关键字的进程
- 在Eclipse中查看JDK类库的源代码
- UVa 808 (建坐标系、找规律) Bee Breeding
- 【C语言】-循环结构-for语句
- hdu 5073 Galaxy
- 《转》JAVA动态代理(JDK和CGLIB)
- mysql经常使用命令总结
- hdu_Anniversary party_(树形DP入门题)
- Java NIO (三) 通道(Channel)
- [开源]基于Log4Net简单实现KafkaAppender
- zTree 3-- jQuery 树插件笔记
- Yarn架构详解
- js dictionary
- The module is an Android project without build variants, and cannot be built
- 好久没玩docker了,温下手
- 查看mysql数据库中的所有用户
- 【Struts2】result类型
热门文章
- Java Web 学习(4) —— Spring MVC 概览
- LeetCode解题笔记 - 3. Longest Substring Without Repeating Characters
- 【ECNU77】位与数对个数(数位DP)
- 《细说PHP》第四版 样章 第18章 数据库抽象层PDO 8
- LinkedTransferQueue
- sqlite3数据库最大可以是多大?可以存放多少数据?读写性能怎么样?
- Hive表导出成csv文件
- Java生鲜电商平台-SpringCloud微服务架构中核心要点和实现原理
- 尉蓝色的P2P金融众筹平台手机模板
- 怎么在CAD中测量图纸距离?来看看这两种方法