高並發下的下單功能設計
功能需求:設計一個秒殺系統初始方案
商品表設計:熱銷商品提供給用戶秒殺,有初始庫存。
@EntitypublicclassSecKillGoodsimplementsSerializable{@IdprivateStringid;/*** 剩餘庫存*/private Integer remainNum;/*** 秒殺商品名稱*/privateStringgoodsName;}
秒殺訂單表設計:記錄秒殺成功的訂單情況
@Entitypublic class SecKillOrder implements Serializable {@Id@GenericGenerator(name ="PKUUID", strategy ="uuid2")@GeneratedValue(generator ="PKUUID")@Column(length =36) private String id;//用戶名稱privateStringconsumer;//秒殺產品編號privateStringgoodsId;//購買數量privateIntegernum;}
Dao設計:主要就是一個減少庫存方法,其他CRUD使用JPA自帶的方法
publicinterfaceSecKillGoodsDaoextendsJpaRepository{@Query("update SecKillGoods g set g.remainNum = g.remainNum - ?2 where g.id=?1")@Modifying(clearAutomatically =true)@TransactionalintreduceStock(String id,Integer remainNum);}
數據初始化以及提供保存訂單的操作:
@ServicepublicclassSecKillService{@AutowiredSecKillGoodsDao secKillGoodsDao;@AutowiredSecKillOrderDao secKillOrderDao;/** * 程序啟動時: * 初始化秒殺商品,清空訂單數據 */@PostConstructpublicvoidinitSecKillEntity(){ secKillGoodsDao.deleteAll(); secKillOrderDao.deleteAll(); SecKillGoods secKillGoods =newSecKillGoods(); secKillGoods.setId("123456"); secKillGoods.setGoodsName("秒殺產品"); secKillGoods.setRemainNum(10); secKillGoodsDao.save(secKillGoods); }/** * 購買成功,保存訂單 *@paramconsumer *@paramgoodsId *@paramnum */publicvoidgenerateOrder(String consumer, String goodsId, Integer num){ secKillOrderDao.save(newSecKillOrder(consumer,goodsId,num)); }}
下面就是controller層的設計
@ControllerpublicclassSecKillController{@AutowiredSecKillGoodsDao secKillGoodsDao;@AutowiredSecKillService secKillService;/*** 普通寫法* @param consumer* @param goodsId* @return*/@RequestMapping("/seckill.html")@ResponseBodypublicStringSecKill(Stringconsumer,StringgoodsId,Integernum) throws InterruptedException {//查找出用戶要買的商品SecKillGoods goods = secKillGoodsDao.findOne(goodsId);//如果有這麼多庫存if(goods.getRemainNum()>=num){//模擬網路延時Thread.sleep(1000);//先減去庫存secKillGoodsDao.reduceStock(num);//保存訂單secKillService.generateOrder(consumer,goodsId,num);return"購買成功"; }return"購買失敗,庫存不足"; }}
上面是全部的基礎準備,下面使用一個單元測試方法,模擬高並發下,很多人來購買同一個熱門商品的情況。
@ControllerpublicclassSecKillSimulationOpController{finalStringtakeOrderUrl ="http://127.0.0.1:8080/seckill.html";/*** 模擬並發下單*/@RequestMapping("/simulationCocurrentTakeOrder")@ResponseBodypublicStringsimulationCocurrentTakeOrder() {//httpClient工廠finalSimpleClientHttpRequestFactory httpRequestFactory =newSimpleClientHttpRequestFactory();//開50個線程模擬並發秒殺下單for(inti =; i
訪問localhost:8080/simulationCocurrentTakeOrder,就可以測試了
預期情況:因為我們只對秒殺商品(123456)初始化了10件,理想情況當然是庫存減少到0,訂單表也只有10條記錄。
實際情況:訂單表記錄
商品表記錄
下面分析一下為啥會出現超庫存的情況:
因為多個請求訪問,僅僅是使用dao查詢了一次資料庫有沒有庫存,但是比較惡劣的情況是很多人都查到了有庫存,這個時候因為程序處理的延遲,沒有及時的減少庫存,那就出現了臟讀。如何在設計上避免呢?最笨的方法是對SecKillController的seckill方法做同步,每次只有一個人能下單。但是太影響性能了,下單變成了同步操作。
@RequestMapping("/seckill.html")@ResponseBodypublic synchronized String SecKill改進方案
根據多線程編程的規範,提倡對共享資源加鎖,在最有可能出現並發爭搶的情況下加同步塊的思想。應該同一時刻只有一個線程去減少庫存。但是這裡給出一個最好的方案,就是利用Oracle,MySQL的行級鎖–同一時間只有一個線程能夠操作同一行記錄,對SecKillGoodsDao進行改造:
publicinterfaceSecKillGoodsDaoextendsJpaRepository{@Query("update SecKillGoods g set g.remainNum = g.remainNum - ?2 where g.id=?1 and g.remainNum>0")@Modifying(clearAutomatically =true)@TransactionalintreduceStock(String id,Integer remainNum);}
僅僅是加了一個and,卻造成了很大的改變,返回int值代表的是影響的行數,對應到controller做出相應的判斷。
@RequestMapping("/seckill.html")@ResponseBodypublicStringSecKill(Stringconsumer,StringgoodsId,Integernum) throws InterruptedException {//查找出用戶要買的商品SecKillGoods goods = secKillGoodsDao.findOne(goodsId);//如果有這麼多庫存if(goods.getRemainNum()>=num){//模擬網路延時Thread.sleep(1000);if(goods.getRemainNum()>) {//先減去庫存inti = secKillGoodsDao.reduceStock(goodsId,num);if(i!=) {//保存訂單secKillService.generateOrder(consumer, goodsId,num);return"購買成功"; }else{return"購買失敗,庫存不足"; } }else{return"購買失敗,庫存不足"; } }return"購買失敗,庫存不足"; }
在看看運行情況
訂單表:
在高並發問題下的秒殺情況,即使存在網路延時,也得到了保障。
點擊展開全文
※Uber醜聞不斷,被曝將問題車租給司機
※VIM使用技巧及快捷操作
※MySQL性能優化的最佳20 條經驗
※php如何解決中文亂碼問題?
※一些MongoDB的坑
TAG:PHP技術大全 |
※設計書單 | 並不是所有的設計師都能設計出好的字體
※高腰的設計上窄下寬,打造出完美的身材線條
※設計書單 | 設計不可能倒退到手工時代,但越來越多的設計師開始回歸最基本的狀態
※再不開發腦洞,在設計界都混不下去了!
※王俊凱設計的球鞋終於要發售了!可能極其限量!
※設計,重「心」出發
※除了上下床難,兒童房還可以這麼設計~
※設計不可能倒退到手工時代,但越來越多的設計師開始回歸最基本的狀態
※6個海報設計思路,初學者掌握了也能做出高大上作品!
※三星發布能隱形的電視,超薄設計,掛起來還可以當畫框!
※魅藍首款高端機魅藍E3售價曝光;設計終究還是向做出了市場妥協
※遍游世界的他,用隨性不羈發掘設計的無限可能
※以後你可能會穿上自己設計的耐克球衣
※極具冷艷與大膽設計!這可能是顏值最高的魅藍手機
※如何設計高性能低側電流感應設計中的印刷電路板
※將電氣設計和機械設計融合在一起能給客戶帶來哪些價值
※中國設計的火箭炮性能都要趕上導彈的水平了,俄羅斯後悔不已
※這些鞋櫃設計太逆天了,顏值與實用並存,收納功能強大
※又一款AI智能音箱發布 懸浮式設計!
※海面上漂浮的垃圾,在設計師的雙手上也能變成一款新型運動鞋