最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

高并發(fā)下單加鎖嗎?

2023-06-24 12:27 作者:劉姥姥看人間  | 我要投稿

一個(gè)簡(jiǎn)單的下單流程包括,商品校驗(yàn),訂單計(jì)價(jià),扣庫(kù)存,保存訂單。其中扣庫(kù)存的并發(fā)問(wèn)題是整個(gè)流程中最麻煩,最復(fù)雜的環(huán)節(jié),可以說(shuō)聚集了所有的智慧和頭發(fā)。

解決扣庫(kù)存并發(fā)問(wèn)題,很容易讓人想到加鎖,加鎖的目的是為了限制同步代碼塊并發(fā),進(jìn)一步的保證原子性,可見(jiàn)性和重排序,實(shí)現(xiàn)數(shù)據(jù)一致性。

單機(jī)加 jvm 鎖,分布式加分布式鎖。這讓我不禁想起分布式系統(tǒng)一句黑話(huà),分布式系統(tǒng)中,沒(méi)有什么問(wèn)題是不能通過(guò)增加中間環(huán)節(jié)解決的,但解決一個(gè)問(wèn)題常常會(huì)帶來(lái)另外的問(wèn)題,是的,你沒(méi)聽(tīng)錯(cuò),以空間換時(shí)間,以并發(fā)換數(shù)據(jù)一致性,在這里,鎖粒度和范圍對(duì)并發(fā)影響是最直接的,設(shè)計(jì)的時(shí)候盡可能的縮小鎖粒度和范圍,一般粒度是 skuId,范圍盡量減小。

鎖時(shí)長(zhǎng),鎖過(guò)期是另外兩個(gè)不得不考慮的問(wèn)題。最麻煩的鎖過(guò)期,常用解決方案是依賴(lài) redission 的看門(mén)狗機(jī)制,相當(dāng)于定時(shí)任務(wù)給鎖續(xù)命,但粗暴續(xù)命會(huì)增加 rt,同時(shí)增加其他請(qǐng)求的阻塞時(shí)長(zhǎng)。

盡量避免犧牲并發(fā)的方案!
盡量避免犧牲并發(fā)的方案!
盡量避免犧牲并發(fā)的方案!

一次偶然的機(jī)會(huì),我的同事向我推薦了 Google 的 chubby。為什么我們不能用悲觀(guān)鎖+樂(lè)觀(guān)鎖的組合呢?在鎖過(guò)期的時(shí)候,樂(lè)觀(guān)鎖兜底,不影響請(qǐng)求 rt,也能保證數(shù)據(jù)一致性。這是個(gè)不錯(cuò)的方案,適合簡(jiǎn)單的場(chǎng)景!

一次偶然的機(jī)會(huì),一條公式?jīng)_擊我的大腦,redis = 高性能 + 原子性。機(jī)智的你肯定知道加鎖就是為了保證原子性,基于 redis 實(shí)現(xiàn)分布式鎖也是因?yàn)?redis 的原子性和高性能(想想什么情況用 mysql 和 zk),如果我用 redis 代替鎖,是不是既能保證扣庫(kù)存的原子性,同時(shí)因?yàn)闆](méi)有鎖,又不需要考慮加鎖帶來(lái)的問(wèn)題。

說(shuō)干就干,馬上畫(huà)個(gè)圖。(圖片被掘金壓縮,有點(diǎn)糊,我上傳到圖床,點(diǎn)擊能跳轉(zhuǎn)到圖床看清晰的,如果看不清楚圖片,聯(lián)系我,給我留言

我把訂單流程分為5大塊,有點(diǎn)復(fù)雜,且聽(tīng)我細(xì)細(xì)道來(lái)。

Order process:

扣庫(kù)存是限制訂單并發(fā)的瓶頸,依靠 redis 的原子性保證數(shù)據(jù)一致性,高性能提升并發(fā)

2pc

基于二階段提交思想,第一步首先插入 INIT 狀態(tài)的訂單

冷熱路由

第二步有個(gè)路由,冷門(mén)商品走 mysql 下單,熱門(mén)商品并發(fā)大,依靠 redis 撐。

如何知道商品冷熱,答案是 bitMap,所以我們還需要一個(gè)定時(shí)任務(wù)(job4)維護(hù) bitMap。冷熱數(shù)據(jù)的統(tǒng)計(jì)來(lái)源一般是購(gòu)物車(chē),埋點(diǎn)統(tǒng)計(jì)。大電商平臺(tái)來(lái)源更豐富,大數(shù)據(jù)追蹤,算法推薦等。

故障處理

lua 扣減庫(kù)存時(shí),需要考慮請(qǐng)求超時(shí)和 redis 宕機(jī)。請(qǐng)求超時(shí)比較好解決,可以 catch 超時(shí)異常,依據(jù)業(yè)務(wù)選擇重試或返回。redis 宕機(jī)比較棘手,后面分析。

降級(jí)

這里說(shuō)一下降級(jí)。redis 宕機(jī)之后,走冷門(mén)訂單流程。但是這里的設(shè)計(jì)會(huì)變的很復(fù)雜,因?yàn)樾枰鉀Q兩個(gè)問(wèn)題,如何斷定 redis 宕機(jī),走冷門(mén)路由會(huì)不會(huì)把 mysql 壓垮?這兩個(gè)問(wèn)題繼續(xù)談?wù)撓氯?huì)衍生出更多,比如走冷門(mén)路由的時(shí)機(jī),冷門(mén)路由會(huì)不會(huì)把 mysql 壓垮等,所以這里觸發(fā)熔斷還需要馬上開(kāi)啟限流,展開(kāi)真的很復(fù)雜,下次有機(jī)會(huì)說(shuō)。

扣庫(kù)存后續(xù)動(dòng)作突然變得順暢,插入訂單庫(kù)存流水,修改訂單狀態(tài) UNPAY,發(fā)送核銷(xiāo)優(yōu)惠券 mq,日志記錄等。這幾個(gè)步驟中,

  • 流水用于記錄訂單和庫(kù)存的綁定,重建商品庫(kù)存緩存會(huì)用到

  • 核銷(xiāo)優(yōu)惠券選擇異步,發(fā)核銷(xiāo)優(yōu)惠券的 mq,需要考慮消息丟失和重復(fù)消費(fèi),上游訂單服務(wù)記錄本地消息表,同時(shí)有個(gè)定時(shí)任務(wù)(job1)掃描重發(fā),下游做好冪等

  • 我們還需要關(guān)注該流程可能會(huì)出現(xiàn) jvm 宕機(jī),這是很?chē)?yán)重的事故,按理說(shuō)沒(méi)有順利走完訂單流程的訂單屬于異常訂單,異常訂單的庫(kù)存需要返還 redis,所以還需要一個(gè)定時(shí)任務(wù)處理異常訂單。

JOB2

redis 沒(méi)有庫(kù)存流水,被扣庫(kù)存 x 無(wú)法得知

訂單流程有幾處宕機(jī)需要考慮,一處是執(zhí)行 lua 腳本時(shí) redis 宕機(jī),另一處是扣完庫(kù)存之后,jvm 宕機(jī)。無(wú)論是 redis 還是 jvm 宕機(jī),這些訂單都會(huì)返回異常信息到前端,所以這些訂單的是無(wú)效的,需要還庫(kù)存到 redis。

mysql 和 redis 的流水描述同一件事情,即記錄該筆訂單所扣庫(kù)存。在異常情況下,可能只有 redis 有流水,依然可以作為斷定庫(kù)存已經(jīng)扣減的依據(jù),在極端異常的情況,lua 腳本剛扣完庫(kù)存,redis 進(jìn)程死了或者宕機(jī),雖然 lua 是原子性的,但宕機(jī)可不是原子性,庫(kù)存 x 已經(jīng)扣了,沒(méi)有流水記錄,無(wú)法知道 x (redis 的單點(diǎn)問(wèn)題可以通過(guò) redis ha 保證)。

如果 redis 恢復(fù)了,但數(shù)據(jù)沒(méi)了,怎么辦?
如果 redis 恢復(fù)了,但數(shù)據(jù)丟失了(庫(kù)存變化還沒(méi)持久化就宕機(jī),redis 重啟恢復(fù)的是舊數(shù)據(jù)),怎么辦?

Rebuild stock cache of sku

剩余庫(kù)存 = (庫(kù)存服務(wù)的總庫(kù)存減去預(yù)占庫(kù)存) - (mysql 和 redis 流水去重,計(jì)算的庫(kù)存)

把目光鎖定到右下角,重建 sku 庫(kù)存緩存的邏輯。一般地,在 redis 扣完庫(kù)存,會(huì)發(fā)個(gè) mq 消息到庫(kù)存服務(wù),持久化該庫(kù)存變動(dòng)。庫(kù)存服務(wù)采用 a/b 庫(kù)存的設(shè)計(jì),分別記錄商品總庫(kù)存和預(yù)占庫(kù)存,為的是解決高并發(fā)場(chǎng)景業(yè)務(wù)操作庫(kù)存和用戶(hù)下單操作庫(kù)存時(shí)的鎖沖突問(wèn)題。庫(kù)存服務(wù)里的庫(kù)存是延遲的,訂單服務(wù)沒(méi)發(fā)的消息和庫(kù)存服務(wù)沒(méi)消費(fèi)的消息造成延遲。

我們既然把庫(kù)存緩存到 redis,不妨想一下如何準(zhǔn)確計(jì)算庫(kù)存的數(shù)量。

  • 在剛開(kāi)始啟動(dòng)服務(wù)的時(shí)候,redis 沒(méi)有數(shù)據(jù),這時(shí)候庫(kù)存 t = a - b(a/b庫(kù)存)

  • 服務(wù)運(yùn)行一段時(shí)間,redis 有庫(kù)存 t, 此時(shí) t = a - b - (庫(kù)存服務(wù)還沒(méi)消費(fèi)的扣庫(kù)存消息),所以拿 mysql 和 redis 的流水去重,計(jì)算出已扣未消費(fèi)庫(kù)存。redis 宕機(jī)后,會(huì)有一個(gè)未知已扣庫(kù)存 x, x 幾乎沒(méi)有算出來(lái)的可能(鄙人盡力了),也沒(méi)必要算出來(lái),你想,當(dāng)你 redis 異常了,庫(kù)存 x 對(duì)應(yīng)的訂單是異常訂單,異常訂單不會(huì)返回給用戶(hù),用戶(hù)只會(huì)收到下單異常的返回,所以庫(kù)存 x 是無(wú)效的,丟掉就好。

Payment process

用戶(hù)支付之后,才發(fā)扣庫(kù)存消息到庫(kù)存服務(wù)落地。落地庫(kù)存服務(wù)的流程很簡(jiǎn)單,不再闡述。重點(diǎn)說(shuō)說(shuō)新增庫(kù)存和減少庫(kù)存。新增庫(kù)存不會(huì)造成超賣(mài),簡(jiǎn)單粗暴的加就好。減少庫(kù)存相當(dāng)于下單,需要小心超賣(mài)問(wèn)題,所以現(xiàn)在 redis 扣了庫(kù)存,再執(zhí)行本地事務(wù),簡(jiǎn)簡(jiǎn)單單,凄凄慘慘戚戚,乍暖還寒時(shí)候,最難將息,三杯兩盞淡酒,咋敵...

多說(shuō)兩句

縱觀(guān)整幅圖,對(duì)比簡(jiǎn)單下單流程,可以發(fā)現(xiàn),為了解決高并發(fā)下單,引入一個(gè)中間環(huán)節(jié),而引入中間環(huán)節(jié)的副作用需要我們處理。雖然訂單流程變復(fù)雜了,但并發(fā)提高了。一般來(lái)說(shuō),redis qps 10萬(wàn),實(shí)際上沒(méi)有10萬(wàn),如果你的業(yè)務(wù) qps 超過(guò)單機(jī) redis 限制,記住,分布式的核心思想就是拆,把庫(kù)存均勻打散到多臺(tái) redis。

打散之后需要解決庫(kù)存傾斜問(wèn)題,可能實(shí)例 a 已經(jīng)賣(mài)完了,實(shí)例 b 還有部分庫(kù)存,但部分用戶(hù)請(qǐng)求打到實(shí)例 a,就會(huì)造成明明有貨,但下單失敗。這個(gè)問(wèn)題也很棘手,感興趣的同學(xué)可以自行研究,學(xué)會(huì)教教我。

上述流程經(jīng)過(guò)簡(jiǎn)化,真實(shí)情況會(huì)更復(fù)雜,不一定適合實(shí)際場(chǎng)景。如果有錯(cuò)誤的地方,煩請(qǐng)留言討論,多多交流,互相學(xué)習(xí),一起進(jìn)步。

還有個(gè)問(wèn)題需要提,流程中的事務(wù)問(wèn)題??梢园l(fā)現(xiàn),訂單流程是沒(méi)有事務(wù)控制的。一方面我們認(rèn)為,數(shù)據(jù)很寶貴,不分正常異常。異常的訂單數(shù)據(jù)可作為分析系統(tǒng)架構(gòu)缺陷的依據(jù),另一方面接口盡量避免長(zhǎng)事務(wù),特別是高并發(fā)下,事務(wù)越短越好。

高并發(fā)下單加鎖嗎?的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
通山县| 开封市| 大邑县| 岱山县| 金乡县| 田阳县| 泾川县| 宿松县| 长垣县| 舟山市| 博罗县| 浦北县| 航空| 桦川县| 拉孜县| 唐山市| 泾川县| 金华市| 眉山市| 呼图壁县| 万山特区| 朝阳区| 湖南省| 墨竹工卡县| 个旧市| 岳普湖县| 盐城市| 来凤县| 达州市| 堆龙德庆县| 毕节市| 梓潼县| 永新县| 北海市| 鲁山县| 嵩明县| 河西区| 平阳县| 东山县| 游戏| 城口县|