關(guān)于秒殺系統(tǒng)中訂單庫(kù)存扣減的最佳實(shí)踐

一、背景

一般在日常開(kāi)發(fā)中經(jīng)常會(huì)遇到打折促銷,秒殺活動(dòng)当船,就如拼多多最近的4999搶券買愛(ài)瘋11促銷活動(dòng)来破,畢竟誰(shuí)的錢也不是大風(fēng)刮來(lái)的,有秒殺有促銷必定帶來(lái)大量用戶货岭,而這類活動(dòng)往往支撐著公司重要營(yíng)銷策略路操,所以保證系統(tǒng)在高并發(fā)下不出異常非常關(guān)鍵,這其中棘手的便是如何在高并發(fā)下高效的處理庫(kù)存數(shù)據(jù)千贯。

現(xiàn)在處理這種場(chǎng)景存在多種方案屯仗。但是要保證高性能和高可用,大部分方案并不滿足搔谴,今天就來(lái)聊聊高并發(fā)下庫(kù)存加減那些事兒魁袜。

二、方案

1. 歷史數(shù)據(jù)庫(kù)的事務(wù)特性和唯一主鍵

基于數(shù)據(jù)庫(kù)的事務(wù),扣減庫(kù)存的操作方法同一個(gè)事務(wù)中進(jìn)行庫(kù)存扣減峰弹,事務(wù)中任何操作失敗店量,執(zhí)行回滾操作。從而保證原子性鞠呈。單純靠數(shù)據(jù)庫(kù)的事務(wù)融师,只能在單體的項(xiàng)目中。如何要分布式的項(xiàng)目中蚁吝,就無(wú)法保證單線程操作了诬滩。

那如何在多進(jìn)程中實(shí)現(xiàn)單線程扣減庫(kù)存呢?我們可以利用數(shù)據(jù)庫(kù)的唯一索引灭将。具體操作步驟:

? 1. 新建立一張表:t_lock_order疼鸟,同時(shí)將商品ID作為唯一索引;

? 2.?進(jìn)行扣減庫(kù)存之前在表中插入商品ID,然后進(jìn)行數(shù)據(jù)庫(kù)更新庙曙;

? 3. 更新結(jié)束后刪除剛才插入數(shù)據(jù)庫(kù)中的記錄空镜。

A線程進(jìn)程扣減庫(kù)存時(shí)候,插入了該商品的ID捌朴,當(dāng)B線程扣減該商品的庫(kù)存的時(shí)候吴攒,同樣也會(huì)在數(shù)據(jù)庫(kù)中插入該商品ID,A線程沒(méi)有執(zhí)行完B線程插入同一個(gè)商品ID就會(huì)報(bào)主鍵重復(fù)的錯(cuò)誤砂蔽,這樣就扣減庫(kù)存失敗洼怔。

這種方案,功能上是可以實(shí)現(xiàn)左驾;但是過(guò)分依賴數(shù)據(jù)庫(kù)镣隶,無(wú)法滿足其性能要求,而且存在很多獲取鎖失敗的情況诡右,用戶體驗(yàn)差安岂。

2. 分布式鎖

Redis?或者?ZooKeeper?來(lái)實(shí)現(xiàn)一個(gè)分布式鎖,以商品維度來(lái)加鎖帆吻,在獲取到鎖的線程中域那,按順序去執(zhí)行商品庫(kù)存的查詢和扣減,這樣就同時(shí)實(shí)現(xiàn)了順序性和原子性猜煮。其實(shí)這個(gè)思路是可以的次员,只是不管通過(guò)哪種方式實(shí)現(xiàn)的分布式鎖,都是有弊端的王带。

以?Redis?的實(shí)現(xiàn)來(lái)說(shuō)淑蔚,通過(guò)超時(shí)時(shí)間來(lái)控制鎖的失效時(shí)間,不太靠譜辫秧,比如在有些場(chǎng)景中束倍,一個(gè)線程 A 獲取到了鎖之后,由于業(yè)務(wù)代碼執(zhí)行時(shí)間可能比較長(zhǎng),導(dǎo)致超過(guò)了鎖的超時(shí)時(shí)間绪妹,自動(dòng)失效甥桂,后續(xù)線程 B 又意外的持有了鎖,當(dāng)線程 A 再次恢復(fù)后邮旷,通過(guò) del 命令釋放鎖黄选,就錯(cuò)誤的將線程 B 中同樣 key 的鎖誤刪除了。

所以婶肩,如果鎖的超時(shí)時(shí)間設(shè)置過(guò)長(zhǎng)办陷,會(huì)影響性能,如果設(shè)置的超時(shí)時(shí)間過(guò)短律歼,有可能業(yè)務(wù)阻塞沒(méi)有處理完成民镜,能否合理設(shè)置超時(shí)時(shí)間,是基于緩存實(shí)現(xiàn)分布式鎖很難解決的一個(gè)問(wèn)題险毁。

那么如何合理設(shè)置超時(shí)時(shí)間呢制圈??你可以基于續(xù)約的方式設(shè)置超時(shí)時(shí)間:先給鎖設(shè)置一個(gè)超時(shí)時(shí)間,然后啟動(dòng)一個(gè)守護(hù)線程畔况,讓守護(hù)線程在一段時(shí)間后鲸鹦,重新設(shè)置這個(gè)鎖的超時(shí)時(shí)間。實(shí)現(xiàn)方式就是:寫一個(gè)守護(hù)線程跷跪,然后去判斷鎖的情況馋嗜,當(dāng)鎖快失效的時(shí)候,再次進(jìn)行續(xù)約加鎖吵瞻,當(dāng)主線程執(zhí)行完成后葛菇,銷毀續(xù)約鎖即可。不過(guò)這種方式實(shí)現(xiàn)起來(lái)相對(duì)復(fù)雜听皿,我建議你結(jié)合業(yè)務(wù)場(chǎng)景熟呛,所以針對(duì)超時(shí)時(shí)間的設(shè)置宽档,要站在實(shí)際的業(yè)務(wù)場(chǎng)景中進(jìn)行衡量尉姨。

3. Redis + lua 腳本

Redis?單線程支持順序操作,而且性能優(yōu)異吗冤,但是不支持事務(wù)回滾又厉。但是通過(guò)?Redis + lua?腳本可以實(shí)現(xiàn)?Redis?操作的原子性。這種方案同時(shí)滿足順序性和原子性的要求了椎瘟。能幫我們實(shí)現(xiàn)?Redis?執(zhí)行?Lua?腳本的命令可以采用EVALSHA覆致,接下來(lái)用代碼實(shí)現(xiàn)它。

1) 核心思路

首先我們根據(jù)庫(kù)存扣減核心操作肺蔚,完成核心?Lua?腳本的編寫煌妈。其主要實(shí)現(xiàn)的功能就是查詢庫(kù)存并判斷庫(kù)存是否充足,如果充足,則做相應(yīng)的扣減操作璧诵,腳本內(nèi)容如下:

2) 業(yè)務(wù)邏輯

然后我們將?Lua?腳本轉(zhuǎn)成字符串汰蜘,并添加腳本預(yù)加載機(jī)制。

預(yù)加載可以有多種實(shí)現(xiàn)方式

1. 一個(gè)是外部預(yù)加載好之宿,生成了?sha1?然后配置到配置中心族操,這樣?Java?代碼從配置中心拉取最新?sha1?即可;

2. 另一種方式是在服務(wù)啟動(dòng)時(shí)比被,來(lái)完成腳本的預(yù)加載色难,并生成單機(jī)全局變量?sha1。

我們這里先采取第二種方式等缀,代碼結(jié)構(gòu)如下圖所示:

以上是將?Lua?腳本轉(zhuǎn)成字符串形式枷莉,并通過(guò)?@PostConstruct?完成腳本的預(yù)加載。然后新增?EVALSHA?方法尺迂,如下圖所示:

方法入?yún)榛顒?dòng)商品庫(kù)存?key?以及單次搶購(gòu)數(shù)量依沮,并在內(nèi)部調(diào)用?Lua?腳本執(zhí)行庫(kù)存扣減操作∏箍瘢看起來(lái)是不是很簡(jiǎn)單危喉?在寫完底層核心方法之后,我們只需要在下單之前州疾,調(diào)用該方法即可辜限,具體如下圖所示:

三、總結(jié)

最后严蓖,我們從技術(shù)的角度分析了庫(kù)存超賣發(fā)生的兩個(gè)原因:

? ?1. 一個(gè)是庫(kù)存扣減涉及到的兩個(gè)核心操作薄嫡,查詢和扣減不是原子操作;

? ?2. 另一個(gè)是高并發(fā)引起的請(qǐng)求無(wú)序颗胡。

在秒殺場(chǎng)景下毫深,因?yàn)椴樵兙彺嬉炔樵償?shù)據(jù)庫(kù)快,一般將庫(kù)存數(shù)放在緩存中毒姨,直接在緩存中扣減庫(kù)存哑蔫。在上面的三個(gè)方案中,小編建議是采用redis+lua的方案弧呐,即利用Redis的單線程原理闸迷,以及提供的原生?EVALSHA?和?SCRIPT LOAD命令來(lái)實(shí)現(xiàn)庫(kù)存扣減的原子性和順序性,并且經(jīng)過(guò)實(shí)測(cè)也確實(shí)能達(dá)到我們的預(yù)期俘枫,且性能良好腥沽,從而有效地解決了秒殺系統(tǒng)所面臨的庫(kù)存超賣挑戰(zhàn)。

最后鸠蚪,如果我的文章對(duì)你有所幫助或者有所啟發(fā)今阳,歡迎關(guān)注公眾號(hào)(微信搜索公眾號(hào):首席架構(gòu)師專欄)师溅,里面有許多技術(shù)干貨,也有我對(duì)技術(shù)的思考和感悟盾舌,還有作為架構(gòu)師的驗(yàn)驗(yàn)分享险胰;關(guān)注后回復(fù) 【面試題】,有我準(zhǔn)備的面試題矿筝、架構(gòu)師大型項(xiàng)目實(shí)戰(zhàn)視頻等福利 起便, 小編會(huì)帶著你一起學(xué)習(xí)、成長(zhǎng)窖维,讓我們一起加油S茏邸!铸史!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鼻疮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子琳轿,更是在濱河造成了極大的恐慌判沟,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崭篡,死亡現(xiàn)場(chǎng)離奇詭異挪哄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)琉闪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門迹炼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人颠毙,你說(shuō)我怎么就攤上這事斯入。” “怎么了蛀蜜?”我有些...
    開(kāi)封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵刻两,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我滴某,道長(zhǎng)磅摹,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任壮池,我火速辦了婚禮偏瓤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘椰憋。我一直安慰自己,他們只是感情好赔退,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布橙依。 她就那樣靜靜地躺著证舟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窗骑。 梳的紋絲不亂的頭發(fā)上女责,一...
    開(kāi)封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音创译,去河邊找鬼抵知。 笑死,一個(gè)胖子當(dāng)著我的面吹牛软族,可吹牛的內(nèi)容都是我干的刷喜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼立砸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掖疮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起颗祝,我...
    開(kāi)封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤浊闪,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后螺戳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搁宾,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年倔幼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了猛铅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凤藏,死狀恐怖奸忽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揖庄,我是刑警寧澤栗菜,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蹄梢,受9級(jí)特大地震影響疙筹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜禁炒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一而咆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幕袱,春花似錦暴备、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)浅妆。三九已至,卻和暖如春障癌,著一層夾襖步出監(jiān)牢的瞬間凌外,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工涛浙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留康辑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓轿亮,卻偏偏與公主長(zhǎng)得像疮薇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哀托,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容