基於Django的樂觀鎖與悲觀鎖解決訂單併發問題的一點淺見
阿新 • • 發佈:2019-02-16
然後就是樂觀鎖查詢了,相比悲觀鎖,樂觀鎖其實並不能稱為是鎖,那麼它是在做什麼事情呢。
其實是在你要進行資料庫操作時先去查詢一次資料庫中商品的庫存,然後在你要更新資料庫中商品庫存時,將你一開始查詢到的庫存數量和商品的ID一起作為更新的條件,當受影響行數返回為0時,說明沒有修改成功,那麼就是說別的程序修改了該資料,那麼你就可以回滾到之前沒有進行資料庫操作的時候,重新查詢,重複之前的操作一定次數,如果超過你設定的次數還是不能修改那麼就直接返回錯誤結果。
該方法只適用於訂單併發較少的情況,如果失敗次數過多,會帶給使用者不良體驗,同時適用該方法要注意資料庫的隔離級別一定要設定為Read Committed 。
最好在使用樂觀鎖之前檢視一下資料庫的隔離級別,mysql中檢視事物隔離級別的命令為
select @@global.tx_isolation;
class OrderCommitView(View):"""樂觀鎖"""
# 開啟事務裝飾器
@transaction.atomic
def post(self,request):
"""訂單併發 ———— 樂觀鎖"""
# 拿到id
goods_ids_str = request.POST.get('goods_ids')
goods_ids = goods_ids_str.split(',')
if len(goods_ids) == 0 :
return JsonResponse({'res':0,'errmsg':'資料不完整'})
# 當前時間字串
now_str = datetime.now().strftime('%Y%m%d%H%M%S')
# 訂單編號
order_id = now_str + str(request.user.id)
# 地址
pay_method = request.POST.get('pay_method')
# 支付方式
address_id = request.POST.get('address_id')
try:
address = Address.objects.get(id=address_id)
except Address.DoesNotExist:
return JsonResponse({'res':1,'errmsg':'地址錯誤'})
# 商品數量
total_count = 0
# 商品總價
total_amount = 0
# 訂單運費
transit_price = 10
# 建立儲存點
sid = transaction.savepoint()
order_info = OrderInfo.objects.create(
order_id = order_id,
user = request.user,
addr = address,
pay_method = pay_method,
total_count = total_count,
total_price = total_amount,
transit_price = transit_price
)
# 獲取redis連線
goods = get_redis_goodsection('default')
# 拼接key
cart_key = 'cart_%d' % request.user.id
for goods_id in goods_ids:
# 嘗試查詢商品
# 此處考慮訂單併發問題,
# redis中取出商品數量
count = goods.hget(cart_key, goods_id)
if count is None:
# 回滾到儲存點
transaction.savepoint_rollback(sid)
return JsonResponse({'res': 3, 'errmsg': '商品不在購物車中'})
count = int(count)
for i in range(3):
# 若存在訂單併發則嘗試下單三次
try:
goods = Goodsgoods.objects.get(id=goods_id) # 不加鎖查詢
# goods = Goodsgoods.objects.select_for_update().get(id=goods_id) # 加互斥鎖查詢
except Goodsgoods.DoesNotExist:
# 回滾到儲存點
transaction.savepoint_rollback(sid)
return JsonResponse({'res':2,'errmsg':'商品資訊錯誤'})
origin_stock = goods.stock
print(origin_stock, 'stock')
print(goods.id, 'id')
if origin_stock < count:
# 回滾到儲存點
transaction.savepoint_rollback(sid)
return JsonResponse({'res':4,'errmsg':'庫存不足'})
# # 商品銷量增加
# goods.sales += count
# # 商品庫存減少
# goods.stock -= count
# # 儲存到資料庫
# goods.save()
# 如果下單成功後的庫存
new_stock = goods.stock - count
new_sales = goods.sales + count
res = Goodsgoods.objects.filter(stock=origin_stock,id=goods_id).update(stock=new_stock,sales=new_sales)
print(res)
if res == 0:
if i == 2:
# 回滾
transaction.savepoint_rollback(sid)
return JsonResponse({'res':5,'errmsg':'下單失敗'})
continue
else:
break
OrderGoods.objects.create(
order = order_info,
goods = goods,
count = count,
price = goods.price
)
# 刪除購物車中記錄
goods.hdel(cart_key,goods_id)
# 累加商品件數
total_count += count
# 累加商品總價
total_amount += (goods.price) * count
# 更新訂單資訊中的商品總件數
order_info.total_count = total_count
# 更新訂單資訊中的總價格
order_info.total_price = total_amount + order_info.transit_price
order_info.save()
# 事務提交
transaction.savepoint_commit(sid)
return JsonResponse({'res':6,'errmsg':'訂單建立成功'})