這篇講解的內容較多。先解決業務問題再解決效能問題。
之前的**中有兩類業務問題:
超賣;若乙個使用者用兩個平台同時秒殺某一商品,可能會出現該使用者秒殺兩件商品的情況。
這兩個問題雖然業務問題很大,但**改動特別小。
//改動前的**
@update
("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #"
)public int reducestock
(miaoshagoods g)
;//改動後的**
@update
("update miaosha_goods set stock_count = stock_count - 1 where goods_id = # and stock_count > 0"
)public int reducestock
(miaoshagoods g)
;//利用資料庫的鎖來保證不會賣超(資料庫本身會保證不會有兩個執行緒同時改一條記錄)
@service
public
class
orderservice
@transactional
public orderinfo createorder
(miaoshauser user, goodsvo goods)
}
可以看到,**中就改動了兩點,一是查詢重複訂單時是在快取中查,第二點是,在資料庫中建立秒殺訂單和訂單詳情後,會在快取中插入使用者id和商品id結合的鍵(值是秒殺訂單),為之後在快取中查詢做準備。
秒殺系統的效能瓶頸一般是在對資料庫的訪問上,若能減少對資料的訪問,就能在很大程度上提高併發量。
大致的思路是:
redis預減庫存減少資料庫的訪問;
記憶體標記減少redis訪問;
請求先入隊緩衝,非同步下單,增強使用者體驗。
具體的**流程是:
系統初始化,把商品庫存數量載入到redis中;
收到請求,先在記憶體標記上判斷商品是否秒殺完畢,若沒有,則進入下一步;
redis預減庫存,庫存不足,直接返回,否則進入下一步;
redis中判斷是否重複下單,若沒有進入下一步;
請求入隊,前端頁面立即返回排隊中;
請求出隊,資料庫中判斷庫存、快取中判斷是否重複秒殺,若都成功進入下一步;
資料庫中減少庫存,資料庫中生成訂單,在快取中生成使用者和商品的鍵以便之後驗證是否重複下單;
客戶端輪詢(前端中實現),是否秒殺成功。
controller層
在這部分主要完成:
系統的初始化,把秒殺商品的庫存數量載入到redis中;
記憶體中判斷商品是否秒殺完、redis預減庫存、判斷是否重複秒殺、入隊操作。
@controller
("/miaosha"
)public
class
miaoshacontroller
implements
initializingbean
for(goodsvo goods : goodslist)
}//第三版,訊息佇列的方式
;//從token中讀使用者資訊
//記憶體標記,減少redis訪問
boolean over = localovermap.
get(goodsid);if
(over)
//預減庫存
long stock = redisservice.
decr
(goodskey.getmiaoshagoodsstock,
""+ goodsid);if
(stock <0)
//判斷是否已經秒殺到了
miaoshaorder order = orderservice.
getmiaoshaorderbyuseridgoodsid
(user.
getid()
, goodsid);if
(order !=
null
)//入隊
miaoshamessage mm =
newmiaoshamessage()
; mm.
setuser
(user)
; mm.
setgoodsid
(goodsid)
; sender.
sendmiaoshamessage
(mm)
;return result.
success(0
);//排隊中
}}
其中,miaoshamessage的定義為:
public
class
miaoshamessage
rabbitmq相關操作
@service
public
class
mqsender
}
@service
public
class
mqreceiver
//判斷是否已經秒殺到了
miaoshaorder order = orderservice.
getmiaoshaorderbyuseridgoodsid
(user.
getid()
, goodsid);if
(order !=
null
)//減庫存 下訂單 寫入秒殺訂單
miaoshaservice.
miaosha
(user, goods);}
}
其中,miaoshaservice.miaosha(user, goods)操作為:
@service
public
class
miaoshaservice
else
}}
目前,優化完成了。當使用者請求到達controller層,在到達資料庫之前,記憶體map和redis會抵擋大部分的資料庫訪問操作,大概只有與秒殺商品個數差不多的請求會進入佇列從而訪問資料庫。
在之前寫的秒殺功能(3)的壓測中,初步版本的tps = 808/sec;
本次的壓測結果為:
tps = 908/sec,略高一些,沒有提高太多,這可能是和硬體條件有關,我的伺服器效能不高。
資料庫情況:
(這篇寫完我是奔潰的,,明明在幾個小時前我就能寫完。。中間開了個組會我掉網了。。我還沒儲存。。於是寫了兩遍。。又拖累了今天的進度,offer君又遠離我一步。。 )
秒殺介面優化 rabbitMq 配置
本篇部落格rabbitmq 的配置安裝是在虛擬機器下centos 6.5 版本中完成,從rabbitmq服務的啟動到客戶端的訪問,確實走了不少坑,這裡詳細說明 0.秒殺優化瓶頸 減少資料庫的訪問 1.系統初始化,把商品庫存數量載入到redis 2.收到請求,redis 預減少庫存,庫存不足,直接返回...
商城秒殺功能實現
令牌機制實現秒殺功能 利用定時任務 或資料庫作業 將某些商品在規定時間之後要開啟秒殺,根據庫存量同步到快取 redis 中。根據每乙個商品產生對應的token數量,1.redis快取採用 中的list資料型別儲存每個商品的令牌。採用list資料型別儲存的原因主要是每乙個執行緒從list中pop時是單...
flask redis實現搶購(秒殺)功能
今天面試了 一家非常高大上的公司,問了我關於redis的實用性問題,但是答的不是很好,所以下午通過再次學習 redis,實現相關實用性功能的一種。對於搶購功能,難點在於 搶購時 由於高併發請求,導致乙個使用者搶購多件商品,庫存量小於訂單量的情況。如下通過redis的hash和list型別實現相關功能...