1. 程式人生 > 其它 >java售票系統程式碼_Java秒殺系統實戰系列-資料庫級別Sql的優化與程式碼的調整

java售票系統程式碼_Java秒殺系統實戰系列-資料庫級別Sql的優化與程式碼的調整

技術標籤:java售票系統程式碼

0ba49b3246ed60b32c0acdca95c3dc9a.gif

本文是“Java秒殺系統實戰系列文章”的第十三篇,從本篇文章開始我們將進入“秒殺程式碼優化”環節,本文將首先從資料庫級別Sql的優化入手,結合調整秒殺相關的部分核心程式碼,實現初步的優化!

上篇文章《Meter壓力測試重現秒殺場景中超賣等問題》我們暴露出了“秒殺介面”在面對高併發請求的場景下所出現的“超賣”、“重複秒殺”等問題,並對相應的問題進行了分析,然後就沒有然後了……(事了拂衣去!)

問題既然落在我們的手裡,那麼身為一名程式猿,那是沒有理由迴避的。通過分析該“秒殺介面”的核心程式碼,可以發現在資料庫層面,其涉及的Sql我們還是可以動一動手腳的!其調整後的“秒殺核心業務邏輯”的完整原始碼如下所示:

//商品秒殺核心業務邏輯的處理-mysql的優化@Overridepublic Boolean killItemV2(Integer killId, Integer userId) throws Exception { Boolean result=false; //TODO:判斷當前使用者是否已經搶購過當前商品 if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){ //A 查詢待秒殺商品詳情 ItemKill itemKill=itemKillMapper.selectByIdV2(killId); //TODO:判斷是否可以被秒殺canKill=1? if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){ //B 扣減庫存-減一 int res=itemKillMapper.updateKillItemV2(killId); //TODO:扣減是否成功?是-生成秒殺成功的訂單,同時通知使用者秒殺成功的訊息 if (res>0){ commonRecordKillSuccessInfo(itemKill,userId); result=true; } } }else{ throw new Exception("您已經搶購過該商品了!"); } return result;}

△向右滑動檢視

首先是對於 註釋A 那裡的調整,即在獲取“秒殺商品詳情”時,我們限定了“可秒殺商品的數量total需要大於0”,其對應的程式碼為:itemKillMapper.updateKillItemV2(killId);完整的動態Sql如下所示:

 SELECT a.*, b.name AS itemName, (CASE WHEN (now() BETWEEN a.start_time AND a.end_time) THEN 1 ELSE 0 END) AS canKill FROM item_kill AS a LEFT JOIN item AS b ON b.id = a.item_id WHERE a.is_active = 1 AND a.id =#{id} AND a.total>0

△向右滑動檢視

然後是 註釋B 對應的優化調整,即在扣減庫存時,我們除了可以保證正常減1的操作之外,還需要保證扣減完之後的數量大於0,即只有在保證扣減完之後的數量大於0之下,該Sql操作後受影響的行數為1,對應的程式碼為:itemKillMapper.updateKillItemV2(killId);其對應的動態Sql如下所示:

 UPDATE item_kill SET total = total - 1 WHERE id = #{killId} AND total>0

至此,我們在秒殺核心業務邏輯的優化層面~資料庫級別Sql的優化 已經搞完了!除此之外,我們還在程式碼層面進行優化,如下所示:

private void commonRecordKillSuccessInfo(ItemKill kill, Integer userId) throws Exception{ //TODO:記錄搶購成功後生成的秒殺訂單記錄 ItemKillSuccess entity=new ItemKillSuccess(); String orderNo=String.valueOf(snowFlake.nextId()); //entity.setCode(RandomUtil.generateOrderCode()); //傳統時間戳+N位隨機數 entity.setCode(orderNo); //雪花演算法 entity.setItemId(kill.getItemId()); entity.setKillId(kill.getId()); entity.setUserId(userId.toString()); entity.setStatus(SysConstant.OrderStatus.SuccessNotPayed.getCode().byteValue()); entity.setCreateTime(DateTime.now().toDate()); //TODO:學以致用,舉一反三 -> 仿照單例模式的雙重檢驗鎖寫法 if (itemKillSuccessMapper.countByKillUserId(kill.getId(),userId) <= 0){ int res=itemKillSuccessMapper.insertSelective(entity); if (res>0){ //TODO:進行非同步郵件訊息的通知=rabbitmq+mail rabbitSenderService.sendKillSuccessEmailMsg(orderNo); //TODO:入死信佇列,用於 “失效” 超過指定的TTL時間時仍然未支付的訂單 rabbitSenderService.sendKillSuccessOrderExpireMsg(orderNo); } }}

△向右滑動檢視

即我們在“使用者秒殺成功生成訂單記錄”的程式碼加入了類似於“單例模式”中的“雙重檢驗鎖”,即在生成訂單記錄,再次判斷一下“當前使用者是否已經真的沒有搶購過該商品”!

在後面的篇章中,我們將開始搬上“中介軟體”這一利器,並結合本文所介紹Sql的優化和調整後的程式碼,徹底解決高併發壓力測試的場景下出現的“庫存超賣”、“重複秒殺”等亂七八糟的問題!

相關視訊教程可私信諮詢。

推薦閱讀:

Java商城秒殺系統的設計與實戰教程(SpringBoot版)

Java秒殺系統實戰系列-構建SpringBoot多模組專案

Java秒殺系統實戰系列-商品秒殺程式碼實戰