場(chǎng)景——訂單
一、訂單超時(shí)的解決方案
描述:購(gòu)物下單后沒(méi)有付款。系統(tǒng)超時(shí)30分鐘自動(dòng)關(guān)閉訂單。
方案:
| 方案 | 場(chǎng)景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
| JDK 自帶的延時(shí)隊(duì)列 | 小型單機(jī)應(yīng)用,非核心業(yè)務(wù) |
實(shí)現(xiàn)簡(jiǎn)單,基于 JDK 自帶 API,無(wú)需額外組件; |
僅適用于單機(jī)環(huán)境,不支持分布式; 數(shù)據(jù)在內(nèi)存中 DelayQueue,服務(wù)重啟后,隊(duì)列中的任務(wù)會(huì)丟失; 無(wú)法應(yīng)對(duì)大量訂單場(chǎng)景,容易造成內(nèi)存溢出。 |
| Redis 的過(guò)期監(jiān)聽(tīng) | 中小規(guī)模分布式應(yīng)用,對(duì)時(shí)效性要求不高的場(chǎng)景 |
實(shí)現(xiàn)相對(duì)簡(jiǎn)單,利用 Redis 的過(guò)期機(jī)制; 支持分布式,可在多節(jié)點(diǎn)部署監(jiān)聽(tīng); 無(wú)需復(fù)雜的任務(wù)調(diào)度邏輯 |
Redis 的過(guò)期刪除并非實(shí)時(shí),存在延遲;(定期刪除、惰性刪除) 監(jiān)聽(tīng)機(jī)制可能存在消息丟失風(fēng)險(xiǎn); 當(dāng)過(guò)期鍵數(shù)量較多時(shí),可能影響 Redis 性能。 |
| 常用★★★基于 MQ 的延時(shí)消息 | 中大規(guī)模分布式應(yīng)用,核心業(yè)務(wù),對(duì)可靠性和時(shí)效性要求較高 |
可靠性高,支持消息持久化,服務(wù)重啟后消息不丟失; 時(shí)效性較好,不同 MQ 能提供不同精度的延時(shí); 支持分布式,可擴(kuò)展性強(qiáng); 能應(yīng)對(duì)高并發(fā)場(chǎng)景 |
需要引入 MQ 組件,增加了系統(tǒng)復(fù)雜度; 部分 MQ 的延時(shí)消息功能需要額外配置或開(kāi)發(fā); 可能存在一定的消息投遞延遲 |
| 常用★★★分布式定時(shí)任務(wù)批處理 | 大規(guī)模分布式應(yīng)用,對(duì)時(shí)效性要求不高,訂單量極大的場(chǎng)景 |
支持分布式,能有效應(yīng)對(duì)海量訂單; 可靠性高,可通過(guò)任務(wù)重試機(jī)制保證處理結(jié)果; 可根據(jù)業(yè)務(wù)需求靈活設(shè)置調(diào)度周期 |
實(shí)現(xiàn)復(fù)雜,需要搭建分布式定時(shí)任務(wù)框架; 時(shí)效性較差,依賴任務(wù)調(diào)度周期,可能存在訂單超時(shí)后一段時(shí)間才被處理的情況; 批量處理時(shí),若處理邏輯復(fù)雜,可能影響性能 |
1 RabbitMQ 的延時(shí)消息:
- 業(yè)務(wù)發(fā)送一個(gè)延時(shí)消息,我們把消息寫(xiě)入到對(duì)應(yīng)的隊(duì)列。
- 可以設(shè)置隊(duì)列的 TTL(存活時(shí)間),當(dāng)消息超時(shí)后,進(jìn)入死信交換機(jī)。
- 定義一個(gè) BizQueue 用來(lái)接收死信消息,進(jìn)行業(yè)務(wù)的消費(fèi)。
??優(yōu)點(diǎn):
-
- 可以支持海量延時(shí)消息,支持分布式處理。
??缺點(diǎn):
-
- 不靈活,只能支持固定延時(shí)等級(jí);
- 使用復(fù)雜,要配置一堆延時(shí)隊(duì)列;
2 RocketMQ 的定時(shí)消息:
- 業(yè)務(wù)發(fā)送一個(gè)定時(shí)消息。
- 到了時(shí)間,RocketMQ 會(huì)通知客戶端去處理。(時(shí)間輪算法)
優(yōu)點(diǎn):
-
- 使用簡(jiǎn)單,和使用普通消息一樣。
- 支持分布式。
- 精度高,支持任意時(shí)刻。
缺點(diǎn):
-
- 定時(shí)時(shí)長(zhǎng)最大值 24 小時(shí)。
- 成本高:每個(gè)訂單需要新增一個(gè)定時(shí)消息,且不會(huì)馬上消費(fèi),給MQ帶來(lái)很大的存儲(chǔ)成本。
- 同一個(gè)時(shí)刻大量消息會(huì)導(dǎo)致消息延遲:定時(shí)消息的實(shí)現(xiàn)邏輯需要先經(jīng)過(guò)定時(shí)存儲(chǔ)等待觸發(fā),定時(shí)時(shí)間到達(dá)后才會(huì)被投遞給消費(fèi)者。因此,如果將大量定時(shí)消息的定時(shí)時(shí)間設(shè)置為同一時(shí)刻,則到達(dá)該時(shí)刻后會(huì)有大量消息同時(shí)需要被處理,會(huì)造成系統(tǒng)壓力過(guò)大,導(dǎo)致消息分發(fā)延遲,影響定時(shí)精度。
3 (推薦)定時(shí)任務(wù)分布式批處理:
利用 SchedulerX 的分布式調(diào)度能力,定期(如每 1 分鐘)觸發(fā)任務(wù),批量掃描數(shù)據(jù)庫(kù)中 “已創(chuàng)建但未支付且已超時(shí)” 的訂單,執(zhí)行關(guān)閉操作(更新?tīng)顟B(tài)、釋放庫(kù)存等)。
關(guān)鍵組建:
實(shí)現(xiàn)流程:
- 任務(wù)配置:在 SchedulerX 控制臺(tái)創(chuàng)建定時(shí)任務(wù);
- 分片邏輯實(shí)現(xiàn):執(zhí)行器接收到任務(wù)后,根據(jù)分片索引拆分訂單 ID 范圍,避免重復(fù)處理;
- 可靠性保障:失敗重試(自定義配置)、冪等性處理、監(jiān)控告警(任務(wù)失敗或超時(shí))
二、10W QPS 如何防止重復(fù)下單?
發(fā)生重復(fù)下單的常見(jiàn)場(chǎng)景:
- 短時(shí)間內(nèi)多次點(diǎn)擊下單按鈕
- 服務(wù)超時(shí)重試
思路:
屬于接口冪等性問(wèn)題,一鎖二判三更新
- 一鎖:先加鎖,可以加分布式鎖、悲觀鎖都可以,但是一定是一個(gè)互斥鎖;
- 二判:進(jìn)行冪等性判斷,可以基于狀態(tài)機(jī)、業(yè)務(wù)流水表、數(shù)據(jù)庫(kù)唯一索引等,進(jìn)行重復(fù)操作的判斷;
- 三更新:對(duì)數(shù)據(jù)進(jìn)行更新,將數(shù)據(jù)進(jìn)行持久化;
方案:
1 請(qǐng)求唯一ID + 數(shù)據(jù)庫(kù)唯一索引約束
-
- 原理:客戶端在請(qǐng)求下單接口的時(shí)候,調(diào)用后端獲取唯一請(qǐng)求ID: requestId,下單時(shí)帶上這個(gè)ID ,服務(wù)端拿這個(gè)請(qǐng)求ID,去數(shù)據(jù)庫(kù)判斷是否重復(fù)請(qǐng)求;
- 場(chǎng)景:低并發(fā),1w 以內(nèi),無(wú)法滿足 10W 并發(fā);
2 Redis 分布式鎖 + 請(qǐng)求唯一ID
-
- 原理:客戶端在請(qǐng)求下單接口的時(shí)候,調(diào)用后端獲取唯一請(qǐng)求ID(后端把這個(gè)ID保存在 Redis),下單時(shí)帶上這個(gè)ID ,服務(wù)端對(duì)請(qǐng)求ID加鎖 ,如果成功加鎖走后續(xù)流程,如果失敗提示服務(wù)在處理重復(fù)提交;
- 場(chǎng)景:可滿足 10W 并發(fā);
3 Redis 分布式鎖 + Token
-
- 原理:不用去后端請(qǐng)求唯一ID,服務(wù)端根據(jù)特定規(guī)則生成 token 存入 Redis,服務(wù)端對(duì) token 加鎖,如果成功加鎖走后續(xù)流程,如果失敗提示服務(wù)在處理重復(fù)提交;
- 生成規(guī)則:應(yīng)用名 + 接口名 + 方法名 + 請(qǐng)求參數(shù)簽名(請(qǐng)求header、body參數(shù),取SHA1值);
三、如何設(shè)計(jì)秒殺系統(tǒng)?

浙公網(wǎng)安備 33010602011771號(hào)