自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

支持每秒上萬單的秒殺扣庫存事務(wù)

開發(fā) 架構(gòu)
本文介紹一種全新的秒殺架構(gòu),解決了秒殺架構(gòu)中的高并發(fā)扣庫存問題。

該架構(gòu)能夠支持每秒超萬單精準(zhǔn)扣庫存,并且在應(yīng)用crash等情況下,也能保證創(chuàng)建訂單和扣減庫存的數(shù)據(jù)最終嚴(yán)格一致。

現(xiàn)有秒殺系統(tǒng)的問題

現(xiàn)有的秒殺架構(gòu),為了支持高并發(fā),通常把庫存放在Redis中,收到訂單請求時,在Redis中進行庫存扣減。這種的設(shè)計,導(dǎo)致創(chuàng)建訂單和庫存扣減不是原子操作,如果兩個操作中間,遇到進程crash等問題,就會導(dǎo)致數(shù)據(jù)不一致。

即使庫存扣減不放在Redis中,而是放在數(shù)據(jù)庫,不一致問題也通常是存在的。業(yè)務(wù)系統(tǒng)為了模塊化,減少耦合,會將庫存服務(wù)與訂單服務(wù)分開。只要是分開的服務(wù),那么數(shù)據(jù)不一致的情況就是無法避免的。

進程crash等問題,雖然發(fā)生的概率不高,但即使占比百分之一,甚至千分之一,都會產(chǎn)生數(shù)據(jù)不一致,例如扣減的庫存量和創(chuàng)建成功的訂單不一致。

庫存與訂單數(shù)據(jù)不一致是必須解決的難題,常見做法是,開發(fā)人員通過訂單數(shù)據(jù),去校準(zhǔn)庫存數(shù)據(jù),這部分的工作非常繁瑣復(fù)雜,耗費大量的開發(fā)工作,而且很多時候需要人工介入,對數(shù)據(jù)進行人工校驗和修復(fù)。

下面我們來看看新架構(gòu)如何優(yōu)雅解決這個問題

整體架構(gòu)

我們明確業(yè)務(wù)場景,我們把秒殺系統(tǒng)的核心要點提取出來,為以下幾點:

  • 用戶進行秒殺,會在某個時間點發(fā)送大量的請求到后端,請求量會大大高于庫存數(shù)量
  • 后端需要保證庫存扣減和訂單創(chuàng)建是最終嚴(yán)格一致的,即使中間發(fā)生進程crash,最終數(shù)據(jù)不會受到影響

本架構(gòu)基于 https://github.com/dtm-labs/dtm,這是一個分布式事務(wù)框架,提供跨服務(wù),跨庫的數(shù)據(jù)一致性解決方案。

上述的場景下,絕大部分扣減庫的描述請求,都會失敗,時序圖如下:

在這個架構(gòu)中,使用了分布式事務(wù)框架dtm。上述的時序圖中,扣減庫存是在Redis中進行的,與dtm相關(guān)的注冊全局事務(wù)和取消全局事務(wù)也是在Redis中處理的,全程依賴Redis,與數(shù)據(jù)庫無關(guān),因此能夠支持極高的并發(fā),從后面的測試數(shù)據(jù)中可以看到,該架構(gòu)可以輕易處理每秒上萬單的秒殺請求。

雖然大部分請求因為扣減庫存失敗而結(jié)束,但是會有一定數(shù)量的請求,扣減庫存成功,這種情況的時序圖如下:

在這個時序圖中,扣減庫存成功后,會進入到訂單服務(wù),進行訂單相關(guān)的創(chuàng)建,以及后續(xù)的支付。在這個新架構(gòu)中,訂單服務(wù)僅需要處理有效訂單,此時并發(fā)量已經(jīng)大幅下降,只需要通過常規(guī)的方法,例如訂單分庫分表、消息隊列削峰處理,就可以輕松解決問題了。

原子操作

在上述的架構(gòu)中,如果在Redis中扣減庫存后,在提交全局事務(wù)前,發(fā)生進程crash,就會導(dǎo)致兩個操作沒有同時完成,那么這種情況后續(xù)會怎么樣?新架構(gòu)如何保證數(shù)據(jù)最終嚴(yán)格一致?這種情況的整個的時序圖如下:

一旦發(fā)生這類進程crash,導(dǎo)致兩個操作過程中斷,那么dtm服務(wù)器會輪詢超時未完成的事務(wù),如果出現(xiàn)已Prepare、未Submit的全局任務(wù),那么他會調(diào)用反查接口,詢問應(yīng)用,庫存扣減是否成功扣減。如果已扣減,則將全局事務(wù)提交,并進行后續(xù)的調(diào)用;如果未扣減,則將全局事務(wù)標(biāo)記為失敗,不再處理。

保證原子操作的原理,以及發(fā)生各種情況dtm的處理策略,可以參考二階段消息,這里不做詳細(xì)的描述。

核心代碼#

秒殺接口的核心代碼如下:

gid := "{a}flash-sale-" + activityID + "-" + uid
msg := dtmcli.NewMsg(DtmServer, gid).

Add(busi.Busi+"/createOrder", gin.H{activity_id: activityID, UID: uid})err := msg.DoAndSubmit(busi.Busi+"/QueryPreparedRedis", func(bb *BranchBarrier) error {

return bb.RedisCheckAdjustAmount(rds, "{a}stock-"+stockID, -1, 86400)})

}
  • 行1: 一般的秒殺活動,一個用戶僅能購買一次,因此按照活動id+用戶id作為全局事務(wù)ID,能夠保證用戶最多生成一個全局事務(wù),最多創(chuàng)建一個訂單
  • 行2: 創(chuàng)建一個二階段消息的對象,填入dtm服務(wù)器地址,以及全局事務(wù)id
  • 行3: 給二階段消息添加一個分支事務(wù),該事務(wù)分支為創(chuàng)建訂單服務(wù)
  • 行4: 調(diào)用二階段消息的DoAndSubmit,該函數(shù)第一個參數(shù)為反查的URL(見上圖中的反查);第二個參數(shù)為一個回調(diào)函數(shù),里面會包含業(yè)務(wù)邏輯。該函數(shù)會執(zhí)行業(yè)務(wù),并在成功后提交全局事務(wù),保證執(zhí)行業(yè)務(wù)和全局事務(wù)的提交是“原子的”
  • 行5: 調(diào)用RedisCheckAdjustAmount,該函數(shù)會進行庫存扣減,這個函數(shù)進行庫存扣減時,如果庫存不夠,則會返回錯誤;如果庫存足夠,則會扣減庫存,并記錄庫存已扣減成功,這樣可以保證這個操作冪等,并且保證后續(xù)的反查能夠獲得正確的結(jié)果

反查的核心代碼如下:

app.GET(BusiAPI+"/QueryPreparedRedis", dtmutil.WrapHandler2(func(c *gin.Context) interface{} {
return MustBarrierFromGin(c).RedisQueryPrepared(rds)
}))

開發(fā)人員編寫反查的邏輯很簡單,對于Redis里面的數(shù)據(jù),只需要復(fù)制粘貼這上面的代碼就行。反查的詳細(xì)原理參考二階段消息,二階段消息的文檔里介紹的是數(shù)據(jù)庫中如何做,而這里則是用Redis來完成類似的反查邏輯,就不詳細(xì)說明了。

性能#

從上面的介紹中,可以看到,對于大部分扣減庫存失敗的請求,只需要進行三個Redis操作,1. 注冊全局事務(wù);2. 扣減庫存;3. 修改全局事務(wù)為以失敗。這個三個操作都是lua腳本實現(xiàn)。一個普通的redis,每秒大約能夠支持6w個lua腳本操作,照此分析,我們的新架構(gòu),理論上每秒能夠支持2w個秒殺請求。我做的性能測試報告顯示,當(dāng)dtm與扣庫存共享一個redis時,每秒可以輕松完成1.2w個秒殺訂單,達(dá)到理論極限值的60%,詳情可以參考后面的性能測試報告

更進一步分析,扣減庫存與全局事務(wù)可以使用不同的Redis,那么

  • 扣減庫存:若由單獨一個Redis來支持,那么扣庫存的理論上限值為6w/s,預(yù)估的實際值為6*0.6=3.6w/s,如果更進一步,采用 Redis6 的多線程IO,可以獲得更高的性能,大約達(dá)到6 * 2.5 * 0.6=9w/s。
  • 全局事務(wù)操作:而這里面的dtm只需要部署多組,或者未來使用集群版,就可以提供遠(yuǎn)超9w/s的支持。
  • 所以采用新架構(gòu)的情況下,預(yù)計可以達(dá)到9w/s的秒殺請求流量

上述的分析還僅僅限于普通云廠商虛擬機上的自己安裝Redis,假如通過簡單的硬件升級,或者使用云廠商提供的Redis,那么Redis能提供更強勁的性能,上述的9w/s還能夠再提高一個臺階。

參考一下阿里雙十一的峰值訂單:58.3萬筆/秒,那么上述預(yù)估的9w/s,幾乎足以應(yīng)對所有的秒殺活動

代碼示例#

完整的可運行的代碼示例,可以參考https://github.com/dtm-labs/dtm-cases/flash

秒殺性能測試詳情

測試的環(huán)境,兩臺阿里云主機,類型為:ecs.hfc5.3xlarge 12核 CPU 3.1GHz/3.4GHz PPS 130萬

  • 一臺機器運行Redis
  • 另一臺機器運行測試程序

測試過程:

準(zhǔn)備Redis#

選擇虛擬機 A 安裝 Redis

apt-get install -y redis# 修改 /etc/redis/redis.conf# bind 127.0.0.1 => 0.0.0.0systemctl redis restart

準(zhǔn)備dtm

選擇虛擬機 B 安裝 dtm

apt update
apt install -y git

wget https://golang.org/dl/go1.17.1.linux-amd64.tar.gz

rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.1.linux-amd64.tar.gz && cp -f /usr/local/go/bin/go /usr/local/bin/go

git clone https://github.com/dtm-labs/dtm.git && cd dtm && git checkout v1.11.0 && cd bench && make

# 修改 dtm/bench/test-flash-sales.sh

# export BUSI_REDIS=localhost:6379 => 虛擬機A 的私網(wǎng)ip

運行測試

sh test-flash-sales.sh

獲取結(jié)果

我的結(jié)果顯示,每秒大約能夠完成1.2w個秒殺請求:

Requests per second:    11970.21 [#/sec] (mean)

小結(jié)

我們提出了一個全新的秒殺架構(gòu),可以保證創(chuàng)建訂單和扣減庫存的原子性,并且預(yù)估可以支撐9w/s的秒殺請求流量。幫助大家更好更快的解決秒殺的業(yè)務(wù)需求。

歡迎訪問我們的項目,并star支持我們:

https://github.com/dtm-labs/dtm

責(zé)任編輯:張燕妮 來源: 分布式事務(wù)
相關(guān)推薦

2022-11-22 17:15:55

高并發(fā)NameNode

2017-06-15 08:02:02

庫存扣減查詢

2024-09-23 08:03:13

2018-05-28 16:11:04

阿里云GTS分布式

2021-03-16 08:21:29

Spark系統(tǒng)并行

2018-12-05 09:20:02

MySQL數(shù)據(jù)庫索引

2010-10-27 09:09:21

NoSQL

2021-08-26 08:24:33

高并發(fā)秒殺系統(tǒng)

2020-11-05 14:12:16

Vue開源項目js框架

2016-11-02 13:51:02

大數(shù)據(jù)工具

2023-07-20 21:41:08

2019-05-05 09:28:59

架構(gòu)數(shù)據(jù)查詢

2019-07-29 14:40:26

架構(gòu)存儲檢索

2019-09-16 09:34:39

2021-11-23 09:00:00

數(shù)據(jù)庫事務(wù)API無代碼

2019-07-16 08:51:03

熱搜新浪微博數(shù)據(jù)

2015-03-31 10:44:26

IT人員上萬月薪招不到IT人才

2013-03-07 09:30:51

云成本分析工具云服務(wù)亞馬遜云服務(wù)

2010-02-22 10:21:22

2023-05-29 14:28:46

數(shù)據(jù)安全自動駕駛
點贊
收藏

51CTO技術(shù)棧公眾號