前提:先把商品詳情和秒殺商品緩存redis中,減少對數(shù)據(jù)庫的訪問(可使用定時任務(wù))
秒殺商品無非就是那幾步(前面還可能會有一些判斷,如用戶是否登錄,一人一單,秒殺時間驗證等)
1一人一單
2.判斷庫存
3.減庫存
4.創(chuàng)建訂單
秒殺需要解決的問題?
1.解決超賣問題
1.1這樣秒殺肯定會出現(xiàn)超賣的情況,所以必須加鎖。加鎖無非就兩種鎖,樂觀鎖和悲觀鎖。
悲觀鎖:直接在1前上鎖即可,這里使用redisson里的可重入鎖
// 獲取分布式鎖對象
RLock lock = redissonClient.getLock("seckillLock");
try {
//等待5秒獲取鎖,執(zhí)行60秒還是沒有釋放鎖就強制釋放
boolean b = lock.tryLock(5L, 60L, TimeUnit.SECONDS);
if (b){
SeckillProductVo seckillProductVo = seckillProductService.find(time, seckillId);
//1.保證庫存足夠
if (seckillProductVo.getStockCount()<=0){
return Result.error(SeckillCodeMsg.SECKILL_STOCK_OVER);
}
//下面兩個方法必須寫在一起避免事務(wù)未提交,未扣減庫存就釋放鎖
//2.扣減庫存
//3.生成訂單
orderInfo=orderInfoService.doSeckill(phone,seckillProductVo);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
1.2樂觀鎖:這個就不需要用代碼演示了
1一人一單
2.判斷庫存
3.減庫存
4.創(chuàng)建訂單
在3.減庫存的時候mysql語句 and 庫存>0即可,MySQL會有自己的行鎖。所以每次只會執(zhí)行一次修改操作。以上兩個方法都不會造成超賣
但是卻未能解決秒殺問題,原因高并發(fā)請求都進入了數(shù)據(jù)庫來查庫,會造成數(shù)據(jù)庫崩潰或阻塞其它mysql的執(zhí)行。
2.重復(fù)下單問題,如何解決?
2.1 在4.創(chuàng)建訂單后,把用戶的唯一標(biāo)識,id或phone存入redis的set中 key->{phone1,phone2}
redisTemplate.opsForSet().isMember(orderKey,phone)返回true或false
流程變成 1.一人一單 2.判斷庫存 3.減庫存 4.創(chuàng)建訂單 5.redisTemplate.opsForSet().add(orderKey,phone);
2.2 但是這樣并不能完全解決重復(fù)下單,比如用戶同時點了兩次請求。極端條件下,a線程進入步驟1的判斷,從1到4 需要進行很多操作,當(dāng)a線程未走到5時,b線程就進來了這樣也會造成重復(fù)下單。所以必須使用數(shù)據(jù)庫的唯一索引進行解決,把用戶唯一標(biāo)識和秒殺商品唯一標(biāo)識做成唯一索引即可解決
也就是在 4.創(chuàng)建訂單 的時候會出現(xiàn)報錯,3庫存減掉之后數(shù)據(jù)回滾即可。
然而redis里的庫存卻和數(shù)據(jù)庫的就不一樣了,redis是無法被回滾的。后面會使用cannal對數(shù)據(jù)庫和redis的庫存進行同步。。。
有些人可能會說了,2.1沒有解決重復(fù)問題,為什么不直接用2.2解決?2.1在大部分情況下都能解決,避免有人多次點擊秒殺對數(shù)據(jù)造成壓力,所以2.1和2.2一起使用才是最佳的選擇
3.流量控制
3.1 必須把請求攔截在數(shù)據(jù)庫之外,如果庫存只有10個,也只有10個線程能最終到達數(shù)據(jù)庫就是一個比較理想的方案
所以把數(shù)據(jù)庫的秒殺商品庫存存入redis中,進行預(yù)售,只有redis里能執(zhí)行
Long res=redisTemplate.increment(key,-1)減1且返回值res不為負(fù)數(shù)
才能進入service,這樣就極大的減少了請求到service層中
3.2 如果同一個秒殺時間段有多個商品 如100個秒殺商品,每個秒殺商品的庫存為10個,那么就會有10*100個請求到達數(shù)據(jù)庫,如果是淘寶的雙11顯然秒殺的商品會更多。那么我們就需要進行流量的削峰控制。
從controller中攔截消息,service取消息的時候慢慢拿,mq能夠占時的讓一部分請求存儲在里面排隊處理,service根據(jù)自己的處理能力去拿。有些人可能會說排隊處理不是很慢?在controller中發(fā)完消息這個請求已經(jīng)可以算是結(jié)束了,用戶不會立即看到搶購結(jié)果。后面經(jīng)過文章來源:http://www.zghlxwxcb.cn/news/detail-701220.html
最終方案文章來源地址http://www.zghlxwxcb.cn/news/detail-701220.html
到了這里,關(guān)于分布式秒殺方案--java的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!