做ppt音樂(lè)怎么下載網(wǎng)站網(wǎng)絡(luò)小說(shuō)排行榜
Redis秒殺超賣(mài)問(wèn)題
- 前言
- 一、出現(xiàn)秒殺超賣(mài)的原因
- 二、超賣(mài)解決方案
- 使用樂(lè)觀鎖解決超賣(mài)問(wèn)題
- 程序中進(jìn)行解決
前言
這是我認(rèn)為b站上最好的redis教程,各方面講解透徹,知識(shí)點(diǎn)覆蓋比較全。
黑馬redis視頻鏈接:B站黑馬redis教學(xué)視頻
本文參考黑馬redis課程筆記
一、出現(xiàn)秒殺超賣(mài)的原因
超賣(mài)問(wèn)題分析:
在我們?cè)写a中是這么寫(xiě)的
if (voucher.getStock() < 1) {// 庫(kù)存不足return Result.fail("庫(kù)存不足!");}//5,扣減庫(kù)存boolean success = seckillVoucherService.update().setSql("stock= stock -1").eq("voucher_id", voucherId).update();if (!success) {//扣減庫(kù)存return Result.fail("庫(kù)存不足!");}
假設(shè)線(xiàn)程1過(guò)來(lái)查詢(xún)庫(kù)存,判斷出來(lái)庫(kù)存大于1,正準(zhǔn)備去扣減庫(kù)存,但是還沒(méi)有來(lái)得及去扣減,此時(shí)線(xiàn)程2過(guò)來(lái),線(xiàn)程2也去查詢(xún)庫(kù)存,發(fā)現(xiàn)這個(gè)數(shù)量一定也大于1,那么這兩個(gè)線(xiàn)程都會(huì)去扣減庫(kù)存,最終多個(gè)線(xiàn)程相當(dāng)于一起去扣減庫(kù)存,此時(shí)就會(huì)出現(xiàn)庫(kù)存的超賣(mài)問(wèn)題。
超賣(mài)原因流程圖:
二、超賣(mài)解決方案
超賣(mài)問(wèn)題是典型的多線(xiàn)程安全問(wèn)題
,針對(duì)這一問(wèn)題的常見(jiàn)解決方案就是加鎖:而對(duì)于加鎖,我們通常有兩種解決方案:
由于加入悲觀鎖,他認(rèn)為安全問(wèn)題一定發(fā)生,所以一定會(huì)獲取鎖進(jìn)行串行執(zhí)行,這樣程序的性能跟吞吐量等受到很大的影響,效率大大降低,不到萬(wàn)不得已不使用悲觀鎖
使用樂(lè)觀鎖解決超賣(mài)問(wèn)題
樂(lè)觀鎖:
會(huì)有一個(gè)版本號(hào),每次操作數(shù)據(jù)會(huì)對(duì)版本號(hào)+1,再提交回?cái)?shù)據(jù)時(shí),會(huì)去校驗(yàn)是否比之前的版本大1 ,如果大1 ,則進(jìn)行操作成功,這套機(jī)制的核心邏輯在于,如果在操作過(guò)程中,版本號(hào)只比原來(lái)大1 ,那么就意味著操作過(guò)程中沒(méi)有人對(duì)他進(jìn)行過(guò)修改,他的操作就是安全的,如果不大1,則數(shù)據(jù)被修改過(guò),當(dāng)然樂(lè)觀鎖還有一些變種的處理方式比如cas
程序中進(jìn)行解決
我們進(jìn)行扣減庫(kù)存時(shí),加入判斷:
boolean success = seckillVoucherService.update().setSql("stock= stock -1") //set stock = stock -1.eq("voucher_id", voucherId).eq("stock",voucher.getStock()).update(); //where id = ? and stock = ?
以上邏輯的核心含義
是:只要我扣減庫(kù)存時(shí)的庫(kù)存和之前我查詢(xún)到的庫(kù)存是一樣的,就意味著沒(méi)有人在中間修改過(guò)庫(kù)存,那么此時(shí)就是安全的,但是以上這種方式通過(guò)測(cè)試發(fā)現(xiàn)會(huì)有很多失敗的情況,失敗的原因在于:在使用樂(lè)觀鎖過(guò)程中假設(shè)100個(gè)線(xiàn)程同時(shí)都拿到了100的庫(kù)存,然后大家一起去進(jìn)行扣減,但是100個(gè)人中只有1個(gè)人能扣減成功,其他的人在處理時(shí),他們?cè)诳蹨p時(shí),庫(kù)存已經(jīng)被修改過(guò)了,所以此時(shí)其他線(xiàn)程都會(huì)失敗.
通俗一點(diǎn)將:
如果100個(gè)線(xiàn)程同時(shí)開(kāi)始進(jìn)行庫(kù)存的扣減,只有一個(gè)線(xiàn)程在拿到100庫(kù)存,這時(shí)他查詢(xún)得到的是100,我扣減時(shí)候的庫(kù)存是100,它可以完美運(yùn)行,這時(shí)候庫(kù)存變成99,剩下的99個(gè)線(xiàn)程開(kāi)始扣減時(shí),他們一開(kāi)始拿到的是庫(kù)存是100,扣減時(shí)發(fā)現(xiàn)庫(kù)存變成了99,所以肯定就不會(huì)扣減,這樣異常率就會(huì)特別高
修改上面的判斷條件:
之前的方式要修改前后都保持一致,但是這樣我們分析過(guò),成功的概率太低,所以我們的樂(lè)觀鎖需要變一下,改成stock大于0 即可
boolean success = seckillVoucherService.update().setSql("stock= stock -1").eq("voucher_id", voucherId).update().gt("stock",0); //where id = ? and stock > 0