秒殺核心設計(減庫存部分)--防超賣與高並發


from: http://www.tuicool.com/articles/Bfa63e6

              商品詳情頁面的靜態化,varnish加速,秒殺商品庫獨立部署服務器這種就略過不講了。只討論庫存部分的優化
      mySQL配置層面的優化可以參考我的這篇文章    《關於mysql innodb引擎性能優化的一點心得》  
  重點設計在數據庫層面。
  2張表:
  第一張:判重表(buy_record),該用戶有沒秒殺過該商品
  字段: id, uid, goods_id, addtime
  第二張表:商品表 goods
  字段: goods_id   goods_num
      方案1:  
  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          and                goods_num>0            ;      
  if(受影響行數<=0)
      拋異常,商品秒完了,回滾。。。
      該方法完美的解決了超賣與select排它鎖導致的並發低的問題,並且4個sql縮減成2個sql語句。極大提升性能  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM