圖文詳解:電商大促期間,如何設(shè)計(jì)"秒殺"架構(gòu)支撐百萬(wàn)級(jí)并發(fā)請(qǐng)求?
今天咱們?cè)賮?lái)聊個(gè)面試場(chǎng)景題中的熱門話題 — 秒殺架構(gòu)如何扛住百萬(wàn)流量。
其實(shí)搭建秒殺架構(gòu)不難,難的是:當(dāng)瞬時(shí)流量遠(yuǎn)超日常 10-100 倍,如何在快速響應(yīng)與系統(tǒng)穩(wěn)定之間找到平衡?
下面就跟隨牛哥一起看下 "一個(gè)又快又穩(wěn)的秒殺架構(gòu),是怎么搭出來(lái)的”。
先拆需求,別上來(lái)就談技術(shù)
面試時(shí)被問(wèn)到秒殺架構(gòu),面試官第一個(gè)想知道的就是:你能不能把模糊的高并發(fā)拆解成可落地的具體指標(biāo)?
其實(shí)拆解并不難,我們分成兩個(gè)維度切入 — 從用戶視角拆業(yè)務(wù),從技術(shù)視角定指標(biāo)。
1. 從用戶視角拆業(yè)務(wù)
從用戶點(diǎn)擊 “立即搶購(gòu)” 到收到成功短信,整個(gè)過(guò)程能拆成 6 個(gè)關(guān)鍵環(huán)節(jié):
圖片
以上每個(gè)環(huán)節(jié)環(huán)環(huán)相扣:資格沒(méi)通過(guò),就不用查庫(kù)存;庫(kù)存不夠,就不用創(chuàng)建訂單。只有把流程拆細(xì)了,才能知道哪里該加攔截、哪里該做異步。
2. 從技術(shù)視角定指標(biāo)
聊完用戶視角,再來(lái)看看技術(shù)視角。就像醫(yī)生看病要測(cè)體溫,系統(tǒng)設(shè)計(jì)也得有健康指標(biāo):
2.1 并發(fā)能力指標(biāo)
雙 11 級(jí)別的秒殺,峰值 QPS 得扛住 100 萬(wàn),核心 TPS 至少得 5 萬(wàn),不然點(diǎn)擊就卡頓。
圖片
2.2 穩(wěn)定性指標(biāo)
用戶點(diǎn)搶購(gòu)后,99% 的請(qǐng)求得在 200ms 內(nèi)有回應(yīng)。而且秒殺這種核心場(chǎng)景,故障恢復(fù)時(shí)間RTO必須 ≤ 5分鐘,畢竟不能讓用戶干等半小時(shí)吧?
圖片
2.3 數(shù)據(jù)一致性指標(biāo)
庫(kù)存得 100% 準(zhǔn)確,超賣 1 件都可能引發(fā)大量投訴。支付和訂單狀態(tài)也得同步快,延遲不能超 10 秒。
2.4 安全指標(biāo)
防刷量得攔住 99% 以上的腳本,不然黃牛 10 秒就搶光 5000 件庫(kù)存。黑名單用戶必須 100% 攔住,絕對(duì)不能讓他們?cè)賲⑴c。
圖片
以上四個(gè)指標(biāo)為秒殺類系統(tǒng)搭建了技術(shù)層面的健康衡量標(biāo)準(zhǔn),后續(xù)設(shè)計(jì)也要以這些量化指標(biāo)為基準(zhǔn)。
架構(gòu)搭建:秒殺系統(tǒng)的四層骨架
需求明確后,就可以搭架構(gòu)了。接入層、流量削峰層、業(yè)務(wù)邏輯層、數(shù)據(jù)層這四層,每層各司其職,缺一不可。
1. 接入層:先把垃圾流量攔在門外
用戶的請(qǐng)求第一個(gè)到的就是接入層,這里要是沒(méi)攔住無(wú)效流量,后面的數(shù)據(jù)庫(kù)、業(yè)務(wù)服務(wù)很快就會(huì)垮。
那該怎么選接入層的技術(shù)方案?
通常選用 「CDN + APISIX 網(wǎng)關(guān)」 組合。因?yàn)镃DN能扛靜態(tài)資源流量,APISIX 比 Nginx 更靈活,適合秒殺這種需要頻繁調(diào)整規(guī)則的場(chǎng)景。
圖片
下面就來(lái)拆解這個(gè)組合:
1.1 CDN 加速
首先是靜態(tài)資源全量加速,商品圖、活動(dòng)文案、倒計(jì)時(shí)動(dòng)畫這些靜態(tài)內(nèi)容,全扔到阿里云CDN。在北京訪問(wèn),就從北京的 CDN 節(jié)點(diǎn)拿數(shù)據(jù),在上海訪問(wèn),就從上海的 CDN 節(jié)點(diǎn)拿數(shù)據(jù),就近響應(yīng)。
圖片
其次是設(shè)計(jì)地域路由,華北用戶優(yōu)先走華北網(wǎng)關(guān)、華東用戶走華東網(wǎng)關(guān),避免跨地域傳輸帶來(lái)的延遲損耗。
圖片
除此,還要注意 CDN 緩存的時(shí)效性:秒殺開(kāi)始前 1 小時(shí),先提前將商品詳情頁(yè)等靜態(tài)頁(yè)面 “預(yù)熱” 到全國(guó) CDN 節(jié)點(diǎn),確?;顒?dòng)開(kāi)啟時(shí)用戶能瞬間加載;活動(dòng)結(jié)束后,立即給頁(yè)面設(shè)置緩存過(guò)期,避免用戶后續(xù)訪問(wèn)時(shí)看到舊的庫(kù)存信息。
圖片
1.2 APISIX 精細(xì)化限流
用 APISIX 的令牌桶插件,可根據(jù)不同維度精準(zhǔn)控制流量,避免 “一刀切” 式限流影響正常用戶體驗(yàn)。
比如單 IP 每秒最多發(fā) 5 個(gè)請(qǐng)求,單用戶 1 分鐘最多點(diǎn) 10 次,沒(méi)登錄的直接攔住。
圖片
2. 流量削峰層:把"流量尖峰"壓平
秒殺流量就像海嘯,前 10 秒可能集中迸發(fā) 80% 的請(qǐng)求,直接打給業(yè)務(wù)系統(tǒng),數(shù)據(jù)庫(kù)肯定扛不住。
這一層的作用就是「緩沖 + 排隊(duì)」,把 "10 秒 100 萬(wàn)請(qǐng)求" 變成 "10 分鐘 100 萬(wàn)請(qǐng)求",讓系統(tǒng)慢慢處理。
圖片
主要通過(guò)以下三個(gè)手段實(shí)現(xiàn):
2.1 消息隊(duì)列緩沖:流量的第一道減壓閥
用戶下單后的請(qǐng)求先丟進(jìn) RocketMQ,業(yè)務(wù)服務(wù)按自己的能力慢慢消費(fèi)。
這里為什么選RocketMQ 而不是 Kafka?
因?yàn)樗С质聞?wù)消息,能保證下單和扣庫(kù)存同步,還能處理失敗的請(qǐng)求,比 Kafka 更適合業(yè)務(wù)場(chǎng)景。但要注意,消息入隊(duì)≠秒殺成功,必須告訴用戶 "正在排隊(duì),別著急"。
圖片
2.2 用戶排隊(duì)機(jī)制:讓等待可視化
光有 MQ 緩沖還不夠,得讓用戶知道自己排在哪?要等多久?
排隊(duì)機(jī)制用Redis的List結(jié)構(gòu)做排隊(duì)隊(duì)列,用戶請(qǐng)求進(jìn)來(lái)時(shí),會(huì)按以下步驟處理排隊(duì)邏輯:
1)加入排隊(duì)隊(duì)列
# 將用戶ID加入商品的排隊(duì)隊(duì)列
LPUSH seckill:queue:{productId} {userId}2) 查詢排隊(duì)位置
# 查詢當(dāng)前隊(duì)列長(zhǎng)度(用戶排隊(duì)位置)
LLEN seckill:queue:{productId}3) 反饋排隊(duì)狀態(tài)
計(jì)算預(yù)估等待時(shí)間,返回給前端 "當(dāng)前排第 58 位,預(yù)計(jì)等待 2 分鐘" 的提示。
這種明確的排隊(duì)反饋能讓用戶知曉等待狀態(tài),有效減少因焦慮導(dǎo)致的重復(fù)刷新行為,可降低 30% 的無(wú)效請(qǐng)求。
2.3 MQ 積壓處理:超預(yù)期時(shí)的應(yīng)急方案
最后 MQ 積壓也是個(gè)大問(wèn)題,需要提前制定規(guī)則。例如制定如下規(guī)則:
平時(shí)消費(fèi)組只開(kāi) 1 個(gè),要是單個(gè) MQ 分區(qū)積壓超 10 萬(wàn)條,就立刻加 2 個(gè)臨時(shí)消費(fèi)組,專門分流處理下單這類核心請(qǐng)求。
圖片
同時(shí)暫停 "秒殺成功通知" 這類非核心消費(fèi),把所有資源讓給核心流程。
3. 業(yè)務(wù)邏輯層:高效處理核心流程
業(yè)務(wù)邏輯層堪稱整個(gè)秒殺系統(tǒng)的“大腦”,既要處理核心業(yè)務(wù)邏輯,又得兼顧速度、準(zhǔn)確性和穩(wěn)定性。
我們選擇 Spring Cloud Alibaba 作為技術(shù)底座,這套生態(tài)把Nacos服務(wù)發(fā)現(xiàn)、Sentinel熔斷降級(jí)、Dubbo RPC調(diào)用等核心能力都打包整合了,省去了自己拼湊組件的麻煩;
圖片
再搭配 Caffeine 本地緩存,存儲(chǔ)用戶等級(jí)、資格狀態(tài)這類高頻訪問(wèn)的小數(shù)據(jù)。
具體怎么設(shè)計(jì)呢?
首先是服務(wù)獨(dú)立拆分 — 我們把秒殺業(yè)務(wù)拆成三個(gè)微服務(wù)。各自獨(dú)立部署、單獨(dú)擴(kuò)容:
- 第一個(gè)是資格校驗(yàn)服務(wù):專門負(fù)責(zé)檢查用戶是否登錄、是否在黑名單、是否已經(jīng)參與過(guò)秒殺;
- 第二個(gè)庫(kù)存扣減服務(wù):專注處理Redis預(yù)扣庫(kù)存和MySQL最終確認(rèn)扣減的邏輯;
- 第三個(gè)訂單生成服務(wù):負(fù)責(zé)創(chuàng)建訂單、對(duì)接支付渠道等后續(xù)流程。
圖片
這樣拆分的好處很明顯:就算訂單服務(wù)臨時(shí)出問(wèn)題,資格校驗(yàn)和庫(kù)存扣減服務(wù)還能正常工作,不會(huì)出現(xiàn) "一掛全掛" 的連鎖故障。
其次是內(nèi)存快速校驗(yàn):把用戶等級(jí)、歷史購(gòu)買記錄等資格校驗(yàn)規(guī)則,全放進(jìn) Caffeine 本地緩存。
緩存的 Key 設(shè)計(jì)成「用戶ID + 商品ID」,value 直接存「是否有資格」的值,過(guò)期時(shí)間設(shè)為30分鐘。
緩存代碼示例:
// 使用Caffeine更新本地緩存(用戶秒殺資格)
caffeineCache.put(
"user:12345:product:67890:qualification"
, true); // true表示用戶12345對(duì)商品67890有秒殺資格
// 從Caffeine緩存查詢用戶秒殺資格
booleanhasQualification= caffeineCache.getIfPresent(
"user:12345:product:67890:qualification"
);查詢時(shí)先查本地緩存,查不到再按 "Redis→數(shù)據(jù)庫(kù)" 的順序校驗(yàn),三級(jí)緩存下來(lái),平均耗時(shí)大大提升。
最后是熱點(diǎn)隔離:像 1 元秒殺手機(jī)這種爆款商品,請(qǐng)求流量大,必須單獨(dú)處理。
比如其他商品用 20 臺(tái)服務(wù)器支撐,爆款就配 50 臺(tái),讓熱門請(qǐng)求和普通請(qǐng)求分開(kāi)處理,避免一個(gè)商品拖垮整個(gè)系統(tǒng)。
圖片
4. 數(shù)據(jù)層:支撐高讀寫
數(shù)據(jù)層是“彈藥庫(kù)”,既要扛住高讀寫,又不能出錯(cuò)。這一層用 Redis Cluster 存熱數(shù)據(jù),MySQL 存冷數(shù)據(jù),Sharding-JDBC 做分庫(kù)分表,分工明確。
4.1 Redis 存熱數(shù)據(jù)
實(shí)時(shí)庫(kù)存、用戶排隊(duì)位置、資格校驗(yàn)結(jié)果全放Redis。用 3 主 3 從、16 個(gè)分片的架構(gòu),單個(gè)分片 QPS 能到 5 萬(wàn),16 個(gè)就是 80 萬(wàn),足夠扛秒殺。
圖片
4.2 MySQL 存冷數(shù)據(jù)
MySQL 存訂單、支付記錄這些要長(zhǎng)久保存的數(shù)據(jù),用 Sharding-JDBC 按商品 ID 哈希分為 8 個(gè)庫(kù),每個(gè)庫(kù)再分 16 張表,總共 128 張表。
圖片
這樣單表數(shù)據(jù)量能控制在 50 萬(wàn)以內(nèi),要是不分表,單表超 1000 萬(wàn)就慢了,查詢速度提升 5 倍。
4.3 讀寫分離
MySQL 主庫(kù)只負(fù)責(zé)創(chuàng)訂單、扣庫(kù)存等寫操作,從庫(kù)負(fù)責(zé)查訂單、查支付狀態(tài)等讀操作。用 Sharding-JDBC 的插件,讀請(qǐng)求自動(dòng)分給從庫(kù),主庫(kù)壓力能降 40%。
圖片
但主從同步有 1-3 秒延遲,你剛下單可能查不到訂單,所以得提示 “訂單創(chuàng)建中,10 秒后再查"。
通過(guò)這四層配合,既扛住了高并發(fā),又保證了數(shù)據(jù)準(zhǔn)確與用戶體驗(yàn)。
關(guān)鍵細(xì)節(jié):這些坑一定要避開(kāi)
架構(gòu)搭好了,還得填“細(xì)節(jié)坑”。秒殺出問(wèn)題,往往不是架構(gòu)不對(duì),而是細(xì)節(jié)沒(méi)處理好,以下幾個(gè)細(xì)節(jié)不僅是面試高頻考點(diǎn),也是線上事故的重災(zāi)區(qū)。
細(xì)節(jié)1:防超賣
面試官常問(wèn):“怎么保證絕對(duì)不超賣?” 這問(wèn)題看似簡(jiǎn)單,實(shí)則藏著并發(fā)編程的“魔鬼細(xì)節(jié)”。
超賣的根源是多個(gè)線程同時(shí)讀庫(kù)存、扣庫(kù)存,導(dǎo)致數(shù)據(jù)不一致。
解決方案是選擇 「Redis預(yù)扣+MySQL樂(lè)觀鎖」。
1.1 Redis 預(yù)扣庫(kù)存
秒殺開(kāi)始前,先把庫(kù)存加載到Redis;用戶下單時(shí),再用Lua腳本原子扣減庫(kù)存,避免并發(fā)問(wèn)題。
Lua 腳本示例:
-- 1. 秒殺開(kāi)始前加載庫(kù)存到Redis
-- KEYS[1] = "seckill:stock:{productId}"(庫(kù)存Key)
-- ARGV[1] = 初始庫(kù)存數(shù)量(如1000)
redis.call("HSET", KEYS[1], "available", ARGV[1])
-- 2. 用戶下單時(shí)原子扣減庫(kù)存(與加載使用相同的KEYS[1])
-- KEYS[1] = "seckill:stock:{productId}"(庫(kù)存Key)
-- ARGV[2] = 購(gòu)買數(shù)量(如1)
local available = redis.call("HGET", KEYS[1], "available")
ifnot available ortonumber(available) < tonumber(ARGV[2]) then
return0-- 庫(kù)存不足,返回0
end
redis.call("HINCRBY", KEYS[1], "available", -tonumber(ARGV[2])) -- 扣減庫(kù)存
return1-- 扣減成功,返回1為什么用Lua腳本?因?yàn)樗鼙WC多個(gè)Redis命令的原子性執(zhí)行,避免中間被其他請(qǐng)求打斷。這是防超賣的第一道防線。
1.2 MySQL 樂(lè)觀鎖最終確認(rèn)
Redis扣減成功后,得落庫(kù)才算數(shù)。庫(kù)存表設(shè)計(jì)時(shí)加個(gè)version字段,扣減時(shí)對(duì)比版本號(hào):
SQL 示例:
-- 扣減邏輯:僅當(dāng)版本號(hào)匹配、庫(kù)存充足時(shí)才更新,同時(shí)版本號(hào)自增
UPDATE seckill_stock
SET
available_stock = available_stock -1,
version = version +1
WHERE
product_id = #{productId}
AND available_stock >=1-- 確保庫(kù)存足夠
AND version = #{version}; -- 樂(lè)觀鎖版本匹配執(zhí)行后看影響行數(shù):
- 若影響行數(shù) = 1 表示 MySQL 庫(kù)存扣減成功,流程正常推進(jìn);
- 若影響行數(shù) = 0 表示其他請(qǐng)求已搶先扣減庫(kù)存,需回滾 Redis 庫(kù)存:
細(xì)節(jié)2:緩存優(yōu)化
緩存是秒殺的性能引擎,但用不好就成定時(shí)炸彈。最常見(jiàn)的緩存問(wèn)題有:穿透、擊穿和雪崩,下面講講怎么解決。
2.1 緩存穿透
緩存穿透表現(xiàn)為:
當(dāng)用戶請(qǐng)求不存在的商品ID 時(shí),緩存與數(shù)據(jù)庫(kù)均無(wú)數(shù)據(jù),請(qǐng)求會(huì)持續(xù)穿透到數(shù)據(jù)庫(kù)。
可以通過(guò)攔截?zé)o效請(qǐng)求解決緩存穿透,核心手段是「布隆過(guò)濾器 + 緩存空值」雙重?cái)r截:
- 布隆過(guò)濾器前置過(guò)濾:秒殺開(kāi)始前,將所有參與秒殺的商品ID加載到布隆過(guò)濾器。請(qǐng)求進(jìn)來(lái)時(shí)先過(guò)過(guò)濾器,若商品ID不存在,直接返回 "商品不存在",從源頭攔截?zé)o效請(qǐng)求;
圖片
- 緩存空值兜底:考慮到布隆過(guò)濾器有約 0.1% 的誤判率,所以要緩存空值兜底,如緩存 productId = 999999 的值為 null,并設(shè)置1分鐘過(guò)期時(shí)間,避免同類無(wú)效請(qǐng)求在 1 分鐘內(nèi)反復(fù)穿透數(shù)據(jù)庫(kù),同時(shí)也減少 Redis 空值緩存的資源占用
圖片
2.2 緩存擊穿
緩存擊穿表現(xiàn)為:
某個(gè)爆款商品的緩存 key 突然過(guò)期,數(shù)十萬(wàn)請(qǐng)求會(huì)瞬間涌向數(shù)據(jù)庫(kù)。
我們要做的就是 "守護(hù)" 熱點(diǎn)key,用「分布式互斥鎖 + 熱點(diǎn)Key永不過(guò)期」應(yīng)對(duì):
- 分布式鎖控制查詢:借助 Redisson 實(shí)現(xiàn)可靠的分布式鎖,第一個(gè)請(qǐng)求拿到鎖后查詢數(shù)據(jù)庫(kù)并更新緩存,這時(shí)候其他請(qǐng)求處于等待的狀態(tài),避免多個(gè)請(qǐng)求并發(fā)查庫(kù)。Java 代碼示例:
RLocklock= redissonClient.getLock("lock:product:" + productId);
try {
// 5秒內(nèi)嘗試獲取鎖,拿到鎖后30秒自動(dòng)釋放(防止死鎖)
if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {
Productproduct= productMapper.selectById(productId);
// 更新緩存,設(shè)置1小時(shí)過(guò)期(僅非熱點(diǎn)Key用,熱點(diǎn)Key后續(xù)特殊處理)
redisTemplate.opsForValue().set("product:" + productId, product, 1, TimeUnit.HOURS);
return product;
} else {
// 未拿到鎖,重試讀取緩存(等待其他請(qǐng)求更新后再查)
return redisTemplate.opsForValue().get("product:" + productId);
}
} finally {
// 確保鎖釋放,避免內(nèi)存泄漏
if (lock.isHeldByCurrentThread()) lock.unlock();
}- 熱點(diǎn)Key永不過(guò)期:對(duì)爆款商品這類熱點(diǎn)Key,代碼層不設(shè)置主動(dòng)過(guò)期時(shí)間,而是用定時(shí)任務(wù)每30分鐘異步查詢數(shù)據(jù)庫(kù)更新緩存,從根本上避免過(guò)期瞬間的流量沖擊。
2.3 緩存雪崩
緩存雪崩則表現(xiàn)為:
大量商品緩存 key 在同一時(shí)間過(guò)期,數(shù)據(jù)庫(kù)會(huì)被集中請(qǐng)求壓垮。
圖片
為了避免大量請(qǐng)求同時(shí)間過(guò)期,我們通過(guò)「過(guò)期時(shí)間隨機(jī)化 + 本地緩存兜底」化解:
- 過(guò)期時(shí)間隨機(jī)化:給每個(gè)緩存的基礎(chǔ)過(guò)期時(shí)間加上 ± 10分鐘的隨機(jī)值,讓緩存失效時(shí)間分散,避免集中過(guò)期;
圖片
- 本地緩存兜底:即便 Redis 緩存過(guò)期,通過(guò)應(yīng)用本地的 Caffeine 緩存兜底,仍能直接返回?cái)?shù)據(jù),無(wú)需請(qǐng)求 Redis。
細(xì)節(jié)3:分布一致性
分布式系統(tǒng)里,絕對(duì)一致是不可能的,網(wǎng)絡(luò)延遲、節(jié)點(diǎn)故障都會(huì)導(dǎo)致數(shù)據(jù)暫時(shí)對(duì)不上,關(guān)鍵是要 「最終一致」,別出現(xiàn) “付了錢沒(méi)訂單”“扣了庫(kù)存沒(méi)下單” 的情況。
為實(shí)現(xiàn)這一目標(biāo),我們主要通過(guò)輕量化 TCC 事務(wù)、消息隊(duì)列最終一致、實(shí)時(shí)對(duì)賬任務(wù)三種方案分層保障
3.1 輕量化 TCC 事務(wù):保障短事務(wù)
秒殺下單是典型的短事務(wù),流程簡(jiǎn)單、執(zhí)行時(shí)間短,用輕量化的 TCC(Try-Confirm-Cancel)事務(wù)再合適不過(guò):
- Try 階段:不直接修改核心數(shù)據(jù),只做資格校驗(yàn)和預(yù)扣 Redis 庫(kù)存
- Confirm 階段:若 Try 階段無(wú)問(wèn)題,正式執(zhí)行核心操作 — 從 MySQL 中扣減實(shí)際庫(kù)存,同時(shí)生成訂單數(shù)據(jù)
- Cancel 階段:若 Try 階段后出現(xiàn)異常,如用戶支付超時(shí)、庫(kù)存不足,則執(zhí)行回滾操作 — 回滾 Redis 庫(kù)存、刪排隊(duì)記錄。
不過(guò) TCC 的難點(diǎn)在于要寫很多補(bǔ)償代碼,比如 Cancel 失敗了要設(shè)置重試確保最終回滾。
3.2 消息隊(duì)列:應(yīng)對(duì)斷連情況
秒殺高峰期可能出現(xiàn)服務(wù)斷連,此時(shí)單靠 TCC 難以保障數(shù)據(jù)一致,需借助 RocketMQ 的事務(wù)消息能力,確保訂單創(chuàng)建與庫(kù)存扣減的最終同步:
訂單創(chuàng)建后,先發(fā)一條事務(wù)消息到 RocketMQ,庫(kù)存服務(wù)讀取消息后執(zhí)行「扣庫(kù)存」操作。要是扣庫(kù)存失敗,消息會(huì)自動(dòng)重試 3 次,3次都失敗后就進(jìn)入死信隊(duì)列,人工處理。
圖片
這樣能確?!赣唵蝿?chuàng)建」 和 「庫(kù)存扣減」 要么都成功,要么都失敗,不會(huì)出現(xiàn)數(shù)據(jù)不一致。
3.3 實(shí)時(shí)對(duì)賬任務(wù):最后一道防線
即使有 TCC 和事務(wù)消息,仍可能因極端場(chǎng)景出現(xiàn) 「Redis 庫(kù)存與 MySQL 庫(kù)存不一致」 的情況。因此需要一套實(shí)時(shí)對(duì)賬任務(wù),修正數(shù)據(jù)偏差:
每分鐘觸發(fā)一次對(duì)賬任務(wù),對(duì)比 Redis 與 MySQL 的庫(kù)存差異:
圖片
- 若差值在合理范圍(如±1),視為正常延遲,無(wú)需處理;
- 若差值超過(guò)10,則以 MySQL 數(shù)據(jù)為準(zhǔn)修復(fù) Redis 庫(kù)存,確保雙端數(shù)據(jù)最終一致。
優(yōu)化方向
優(yōu)化不是炫技,而是解決用戶痛點(diǎn)、降低成本,"讓用戶搶得爽、讓公司少花錢" 這才是優(yōu)化的價(jià)值,我們從三個(gè)優(yōu)化方向切入:
優(yōu)化1:無(wú)效請(qǐng)求提前過(guò)濾
秒殺場(chǎng)景下,90%的請(qǐng)求都是無(wú)效的,比如重復(fù)點(diǎn)擊、沒(méi)資格、庫(kù)存不足,提前過(guò)濾能“減負(fù)增效”。因此需要在接入層、應(yīng)用層各加過(guò)濾規(guī)則:
過(guò)濾階段 | 規(guī)則示例 | 過(guò)濾效果 |
接入層 | 單IP每秒>5請(qǐng)求、User-Agent異常(腳本標(biāo)識(shí)) | 擋掉30%無(wú)效請(qǐng)求 |
應(yīng)用層 | 未登錄用戶、黑名單用戶、已秒殺用戶 | 擋掉50%無(wú)效請(qǐng)求 |
以「已秒殺用戶過(guò)濾」 為例,我們用 Redis 的 Set 結(jié)構(gòu)記錄參與過(guò)的用戶,用戶每次下單前,先通過(guò)SISMEMBER命令判斷是否在 Set 中,若存在,直接返回 "您已參與過(guò)本次秒殺",僅這一條規(guī)則就能讓重復(fù)下單請(qǐng)求降低 80%。
優(yōu)化2:異步處理提速
用戶下單后,發(fā)短信、推App通知、更新用戶積分,這些操作用戶不關(guān)心實(shí)時(shí)性,完全可以異步處理。
我們用 RocketMQ 的普通消息隊(duì)列(非事務(wù)消息),下單成功后直接向 RocketMQ 發(fā)送一條普通消息,后臺(tái)服務(wù)根據(jù)自身處理能力逐步消費(fèi)。
圖片
優(yōu)化3:跟著流量彈性擴(kuò)容
云時(shí)代了,還手動(dòng)擴(kuò)容就太原始了。用 K8s 的 HPA 自動(dòng)擴(kuò)縮容,資源跟著流量變:秒殺前 10 分鐘,HPA 會(huì)自動(dòng)擴(kuò)到 10 個(gè)實(shí)例;活動(dòng)結(jié)束 5 分鐘,CPU 降下來(lái)了,又縮回 2 個(gè)。
圖片
既保證性能,又不浪費(fèi)資源,每月能省 40% 的服務(wù)器成本。
除此以外,監(jiān)控和容災(zāi)也必不可少,“三分技術(shù),七分運(yùn)維”。因此需要通過(guò)全鏈路監(jiān)控、混沌工程演練、降級(jí)預(yù)案三者結(jié)合筑牢系統(tǒng)穩(wěn)定性。
總結(jié):秒殺架構(gòu)的設(shè)計(jì)心法
回顧整個(gè)秒殺架構(gòu)設(shè)計(jì),我提煉出了四個(gè)核心原則:
1. 流量攔截要“前置”:CDN擋靜態(tài)、網(wǎng)關(guān)限流量、排隊(duì)篩用戶,把無(wú)效請(qǐng)求擋在越上游越好,別讓它們“走到數(shù)據(jù)庫(kù)門口才被攔下”。
2. 數(shù)據(jù)一致是“底線”:Redis原子扣減、MySQL樂(lè)觀鎖、TCC事務(wù)、實(shí)時(shí)對(duì)賬,這四重保障缺一不可,超賣1件,對(duì)用戶來(lái)說(shuō)就是“整個(gè)活動(dòng)不可信”。
3. 監(jiān)控容災(zāi)“不偷懶”:全鏈路監(jiān)控、混沌演練、降級(jí)預(yù)案,這三件事做扎實(shí)了,線上出問(wèn)題也能兜底。
4. 持續(xù)優(yōu)化“不停步”:沒(méi)有“一勞永逸”的秒殺系統(tǒng),流量變了、業(yè)務(wù)變了,架構(gòu)就得跟著變。去年的方案今年可能就不適用,持續(xù)迭代,才能真正做好秒殺。
最后想說(shuō),秒殺架構(gòu)要把用戶體驗(yàn)放在第一位,把數(shù)據(jù)安全當(dāng)作底線,這樣設(shè)計(jì)出來(lái)的系統(tǒng),才能真正扛住“雙11”的流量洪峰,也才能在面試中“打動(dòng)面試官”。






























