秒殺核心設計(減庫存部分)-防超賣與高併發 --學習筆記

商品詳情頁面的靜態化,varnish加速,秒殺商品庫獨立部署服務器這種就略過不講了。只討論庫存部分的優化

重點設計在數據庫層面。

2張表:

第一張:判重表(buy_record),該用戶有沒秒殺過該商品

字段: id, uid, goods_id, addtime

第二張表:商品表 goods

字段: goods_id   goods_num

方案一:

start transaction;

select id from buy_record where uid=$uid and goods_id=$goods_id;

if(結果不爲空)

    拋異常,回滾。

insert into buy_record。。。

if(受影響行數<=0)

    拋異常,回滾。。。

select goods_num from goods where goods_id=$good_id;

if(庫存<=0)

    拋異常,回滾。。。

update goods set goods_num=goods_num-1 where goods_id=$goods_id;

if(受影響行數<=0)

    拋異常,回滾。。。

該方法在高併發下幾乎必然導致超賣。當庫存爲1的時候剛好多個用戶同時 select goods_num from goods where goods_id=$good_id;此時庫存剛好大於0,做update操作的時候必然減到小於0.  同時上面進行是否秒殺過的判重同樣會出現類似問題

方案二:

start transaction;

select id from buy_record where uid=$uid and goods_id=$goods_id for  update ;

if(結果不爲空)

    拋異常,回滾。

insert into buy_record。。。

if(受影響行數<=0)

    拋異常,回滾。。。

select goods_num from goods where goods_id=$good_id for update ;

if(庫存<=0)

    拋異常,回滾。。。

update goods set goods_num=goods_num-1  where goods_id=$goods_id ;

if(受影響行數<=0)

    拋異常,回滾。。。

該方法有效的防止了超賣,但是在每次select的時候加上了排它鎖,每次select操作都會被堵塞 ,併發性能大大降低。

方案三:    對(uid,goods_id)加唯一索引!!

start transaction;

insert into buy_record。。。

if(唯一索引報錯?)

    拋異常,已經秒過了,回滾。。。

update goods set goods_num=goods_num-1  where goods_id=$goods_id andgoods_num>0 ;

if(受影響行數<=0)

    拋異常,商品秒完了,回滾。。。

該方法完美的解決了超賣與select排它鎖導致的併發低的問題,並且4個sql縮減成2個sql語句。極大提升性能

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章