欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis高并發(fā)情況下并發(fā)扣減庫存項(xiàng)目實(shí)戰(zhàn)

 更新時(shí)間:2022年04月14日 10:19:51   作者:Java糖糖  
本文主要介紹了Redis高并發(fā)情況下并發(fā)扣減庫存項(xiàng)目實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

相信大家從網(wǎng)上學(xué)習(xí)項(xiàng)目大部分人第一個(gè)項(xiàng)目都是電商,生活中時(shí)時(shí)刻刻也會(huì)用到電商APP,例如淘寶,京東等。做技術(shù)的人都知道,電商的業(yè)務(wù)邏輯簡單,但是大部分電商都會(huì)涉及到高并發(fā)高可用,對(duì)并發(fā)和對(duì)數(shù)據(jù)的處理要求是很高的。這里我今天就講一下高并發(fā)情況下是如何扣減庫存的?

我們對(duì)扣減庫存所需要關(guān)注的技術(shù)點(diǎn)如下:

  • 當(dāng)前剩余的數(shù)量大于等于當(dāng)前需要扣減的數(shù)量,不允許超賣
  • 對(duì)于同一個(gè)數(shù)據(jù)的數(shù)量存在用戶并發(fā)扣減,需要保證并發(fā)的一致性
  • 需要保證可用性和性能,性能至少是秒級(jí)
  • 一次的扣減包含多個(gè)目標(biāo)數(shù)量
  • 當(dāng)次扣減有多個(gè)數(shù)量時(shí),其中一個(gè)扣減不成功即不成功,需要回滾
  • 必須有扣減才能有歸還
  • 返還的數(shù)量必須要加回,不能丟失
  • 一次扣減可以有多次返還
  • 返還需要保證冪等性

第一種方案:純MySQL扣減實(shí)現(xiàn)

顧名思義,就是扣減業(yè)務(wù)完全依賴MySQL等數(shù)據(jù)庫來完成。而不依賴一些其他的中間件或者緩存。純數(shù)據(jù)庫實(shí)現(xiàn)的好處就是邏輯簡單,開發(fā)以及部署成本低。(適用于中小型電商)。

純數(shù)據(jù)庫的實(shí)現(xiàn)之所以能夠滿足扣減業(yè)務(wù)的各項(xiàng)功能要求,主要依賴兩點(diǎn):

  • 基于數(shù)據(jù)庫的樂觀鎖方式保證并發(fā)扣減的強(qiáng)一致性
  • 基于數(shù)據(jù)庫的事務(wù)實(shí)現(xiàn)批量扣減失敗進(jìn)行回滾

基于上述方案,它包含一個(gè)扣減服務(wù)和一個(gè)數(shù)量數(shù)據(jù)庫

如果數(shù)據(jù)量單庫壓力很大,也可以做主從和分庫分表,服務(wù)可以做集群等。

一次完整的流程就是先進(jìn)行數(shù)據(jù)校驗(yàn),在其中做一些參數(shù)格式校驗(yàn),這里做接口開發(fā)的時(shí)候,要保持一個(gè)原則就是不信任原則,一切數(shù)據(jù)都不要相信,都需要做校驗(yàn)判斷。其次,還可以進(jìn)行庫存扣減的前置校驗(yàn)。比如當(dāng)前庫存中的庫存只有8個(gè),而用戶要購買10個(gè),此時(shí)的數(shù)據(jù)校驗(yàn)中即可前置攔截,減少對(duì)于數(shù)據(jù)庫的寫操作。純讀不會(huì)加鎖,性能較高,可以采用此種方式提升并發(fā)量。

update xxx set leavedAmount=leavedAmount-currentAmount where skuid='xxx' and leavedAmount>=currentAmount

此SQL采用了類似樂觀鎖的方式實(shí)現(xiàn)了原子性。在where后面判斷剩余數(shù)量大于等于需要的數(shù)量,才能成功,否則失敗。

扣減完成之后,需要記錄流水?dāng)?shù)據(jù)。每一次扣減的時(shí)候,都需要外部用戶傳入一個(gè)uuid作為流水編號(hào),此編號(hào)是全局唯一的。用戶在扣減時(shí)傳入唯一的編號(hào)有兩個(gè)作用:

  • 當(dāng)用戶歸還數(shù)量時(shí),需要帶回此編碼,用來標(biāo)識(shí)此次返還屬于歷史上的哪次扣減。
  • 進(jìn)行冪等性控制。當(dāng)用戶調(diào)用扣減接口出現(xiàn)超時(shí)時(shí),因?yàn)橛脩舨恢朗欠癯晒?,用戶可以采用此編?hào)進(jìn)行重試或反查。在重試時(shí),使用此編號(hào)進(jìn)行標(biāo)識(shí)防重

當(dāng)用戶只購買某個(gè)商品一個(gè)的時(shí)候,如果校驗(yàn)時(shí)剩余庫存有8個(gè),此時(shí)校驗(yàn)通過。但在后續(xù)的實(shí)際扣減時(shí),因?yàn)槠渌脩粢苍诓l(fā)的扣減,可能會(huì)出現(xiàn)幻讀,此時(shí)用戶實(shí)際去扣減時(shí)不足一個(gè),導(dǎo)致失敗。這種場景會(huì)導(dǎo)致多一次數(shù)據(jù)庫查詢,降低整體的扣減性能。這時(shí)候可以對(duì)MySQL架構(gòu)進(jìn)行升級(jí)

MySQL架構(gòu)升級(jí)

多一次查詢,就會(huì)增加數(shù)據(jù)庫的壓力,同時(shí)對(duì)整體性能也有一定的影響。此外,對(duì)外提供的查詢庫存數(shù)量的接口也會(huì)對(duì)數(shù)據(jù)庫產(chǎn)生壓力,同時(shí)讀的請(qǐng)求要遠(yuǎn)大于寫。

根據(jù)業(yè)務(wù)場景分析,讀庫存的請(qǐng)求一般是顧客瀏覽商品時(shí)產(chǎn)生,而調(diào)用扣減庫存的請(qǐng)求基本上是用戶購買時(shí)才觸發(fā)。用戶購買請(qǐng)求的業(yè)務(wù)價(jià)值比讀請(qǐng)求會(huì)更大,因此對(duì)于寫需要重點(diǎn)保障。針對(duì)上述的問題,可以對(duì)MySQL整體架構(gòu)進(jìn)行升級(jí)

整體的升級(jí)策略采用讀寫分離的方式,另外主從復(fù)制直接使用MySQL等數(shù)據(jù)庫已有的功能,改動(dòng)上非常小,只要在扣減服務(wù)里配置兩個(gè)數(shù)據(jù)源。當(dāng)客戶查詢剩余庫存,扣減服務(wù)中的前置校驗(yàn)時(shí),讀取從數(shù)據(jù)庫即可。而真正的數(shù)據(jù)扣減還是使用主數(shù)據(jù)庫。

讀寫分離之后,根據(jù)二八原則,80% 的均為讀流量,主庫的壓力降低了 80%。但采用了讀寫分離也會(huì)導(dǎo)致讀取的數(shù)據(jù)不準(zhǔn)確的問題,不過庫存數(shù)量本身就在實(shí)時(shí)變化,短暫的差異業(yè)務(wù)上是可以容忍的,最終的實(shí)際扣減會(huì)保證數(shù)據(jù)的準(zhǔn)確性。

在上面基礎(chǔ)上,還可以升級(jí),增加緩存

純數(shù)據(jù)庫的方案雖然可以避免超賣和少賣的情況,但是并發(fā)量實(shí)在很低,性能不是很樂觀。所以這里再進(jìn)行升級(jí)

第二種方案:緩存實(shí)現(xiàn)扣減

這和前面的扣減庫存其實(shí)是一樣的。但是此時(shí)扣減服務(wù)依賴的是Redis而不是數(shù)據(jù)庫了。

這里針對(duì)Redis的hash結(jié)構(gòu)不支持多個(gè)key的批量操作問題,我們可以采用Redis+lua腳本來實(shí)現(xiàn)批量扣減單線程請(qǐng)求。

升級(jí)成純Redis實(shí)現(xiàn)扣減也會(huì)有問題

  • Redis掛了,如果還沒有執(zhí)行到扣減Redis里面庫存的操作掛了,只需要返回給客戶端失敗即可。如果已經(jīng)執(zhí)行到Redis扣減庫存之后掛了。那這時(shí)候就需要有一個(gè)對(duì)賬程序。通過對(duì)比Redis與數(shù)據(jù)庫中的數(shù)據(jù)是否一致,并結(jié)合扣減服務(wù)的日志。當(dāng)發(fā)現(xiàn)數(shù)據(jù)不一致同時(shí)日志記錄扣減失敗時(shí),可以將數(shù)據(jù)庫比Redis多的庫存數(shù)據(jù)在Redis進(jìn)行加回。
  • Redis扣減完成,異步刷新數(shù)據(jù)庫失敗了。此時(shí)Redis里面的數(shù)據(jù)是準(zhǔn)的,數(shù)據(jù)庫的庫存是多的。在結(jié)合扣減服務(wù)的日志確定是Redis扣減成功到但異步記錄數(shù)據(jù)失敗后,可以將數(shù)據(jù)庫比Redis多的庫存數(shù)據(jù)在數(shù)據(jù)庫中進(jìn)行扣減。

雖然使用純Redis方案可以提高并發(fā)量,但是因?yàn)镽edis不具備事務(wù)特性,極端情況下會(huì)存在Redis的數(shù)據(jù)無法回滾,導(dǎo)致出現(xiàn)少賣的情況。也可能發(fā)生異步寫庫失敗,導(dǎo)致多扣的數(shù)據(jù)再也無法找回的情況。

第三種方案:數(shù)據(jù)庫+緩存 順序?qū)懙男阅芨?/h2>

在向磁盤進(jìn)行數(shù)據(jù)操作時(shí),向文件末尾不斷追加寫入的性能要遠(yuǎn)大于隨機(jī)修改的性能。因?yàn)閷?duì)于傳統(tǒng)的機(jī)械硬盤來說,每一次的隨機(jī)更新都需要機(jī)械鍵盤的磁頭在硬盤的盤面上進(jìn)行尋址,再去更新目標(biāo)數(shù)據(jù),這種方式十分消耗性能。而向文件末尾追加寫入,每一次的寫入只需要磁頭一次尋址,將磁頭定位到文件末尾即可,后續(xù)的順序?qū)懭氩粩嘧芳蛹纯伞?/p>

對(duì)于固態(tài)硬盤來說,雖然避免了磁頭移動(dòng),但依然存在一定的尋址過程。此外,對(duì)文件內(nèi)容的隨機(jī)更新和數(shù)據(jù)庫的表更新比較類似,都存在加鎖帶來的性能消耗。

數(shù)據(jù)庫同樣是插入要比更新的性能好。對(duì)于數(shù)據(jù)庫的更新,為了保證對(duì)同一條數(shù)據(jù)并發(fā)更新的一致性,會(huì)在更新時(shí)增加鎖,但加鎖是十分消耗性能的。此外,對(duì)于沒有索引的更新條件,要想找到需要更新的那條數(shù)據(jù),需要遍歷整張表,時(shí)間復(fù)雜度為 O(N)。而插入只在末尾進(jìn)行追加,性能非常好。

順序?qū)懙募軜?gòu)

通過上面的理論就可以得出一個(gè)兼具性能和高可靠的扣減架構(gòu)

上述的架構(gòu)和純緩存的架構(gòu)區(qū)別在于,寫入數(shù)據(jù)庫不是異步寫入,而是在扣減的時(shí)候同步寫入。同步寫入數(shù)據(jù)庫使用的是insert操作,就是順序?qū)?,而不是update做數(shù)據(jù)庫數(shù)量的修改,所以,性能會(huì)更好。

insert 的數(shù)據(jù)庫稱為任務(wù)庫,它只存儲(chǔ)每次扣減的原始數(shù)據(jù),而不做真實(shí)扣減(即不進(jìn)行 update)。它的表結(jié)構(gòu)大致如下:

create table task{
  id bigint not null comment "任務(wù)順序編號(hào)",
  task_id bigint not null 
}

任務(wù)表里存儲(chǔ)的內(nèi)容格式可以為 JSON、XML 等結(jié)構(gòu)化的數(shù)據(jù)。以 JSON 為例,數(shù)據(jù)內(nèi)容大致可以如下:

{
  "扣減號(hào)":uuid,
  "skuid1":"數(shù)量",
  "skuid2":"數(shù)量",
  "xxxx":"xxxx"
}

這里我們肯定是還有一個(gè)記錄業(yè)務(wù)數(shù)據(jù)的庫,這里存儲(chǔ)的是真正的扣減名企和SKU的匯總數(shù)據(jù)。對(duì)于另一個(gè)庫里面的數(shù)據(jù),只需要通過這個(gè)表進(jìn)行異步同步就好了。

扣減流程

這里和純緩存的區(qū)別在于增加了事務(wù)開啟與回滾的步驟,以及同步的數(shù)據(jù)庫寫入流程

任務(wù)庫里存儲(chǔ)的是純文本的 JSON 數(shù)據(jù),無法被直接使用。需要將其中的數(shù)據(jù)轉(zhuǎn)儲(chǔ)至實(shí)際的業(yè)務(wù)庫里。業(yè)務(wù)庫里會(huì)存儲(chǔ)兩類數(shù)據(jù),一類是每次扣減的流水?dāng)?shù)據(jù),它與任務(wù)表里的數(shù)據(jù)區(qū)別在于它是結(jié)構(gòu)化,而不是 JSON 文本的大字段內(nèi)容。另外一類是匯總數(shù)據(jù),即每一個(gè) SKU 當(dāng)前總共有多少量,當(dāng)前還剩余多少量(即從任務(wù)庫同步時(shí)需要進(jìn)行扣減的),表結(jié)構(gòu)大致如下:

create table 流水表{
  id bigint not null,
  uuid bigint not null comment '扣減編號(hào)',
  sku_id bigint not null comment '商品編號(hào)',
  num int not null comment '當(dāng)次扣減的數(shù)量' 
}comment '扣減流水表'

商品的實(shí)時(shí)數(shù)據(jù)匯總表,結(jié)構(gòu)如下:

create table 匯總表{
  id bitint not null,
  sku_id unsigned bigint not null comment '商品編號(hào)',
  total_num unsigned int not null comment '總數(shù)量',
  leaved_num unsigned int not null comment '當(dāng)前剩余的商品數(shù)量'
}comment '記錄表'

在整體的流程上,還是復(fù)用了上一講純緩存的架構(gòu)流程。當(dāng)新加入一個(gè)商品,或者對(duì)已有商品進(jìn)行補(bǔ)貨時(shí),對(duì)應(yīng)的新增商品數(shù)量都會(huì)通過 Binlog 同步至緩存里。在扣減時(shí),依然以緩存中的數(shù)量為準(zhǔn)

到此這篇關(guān)于Redis高并發(fā)情況下并發(fā)扣減庫存項(xiàng)目實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Redis 并發(fā)扣減庫存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis 存取 JSON 數(shù)據(jù)示例操作

    Redis 存取 JSON 數(shù)據(jù)示例操作

    JSON 是我們常用的數(shù)據(jù)類型,當(dāng)我們需要在Redis中保存json數(shù)據(jù)時(shí)是怎么存放的呢?一般是用String或者Hash,但還是不太方便,無法靈活的操作json 數(shù)據(jù),下面通過本文給大家介紹Redis存取JSON 數(shù)據(jù)示例操作,感興趣的的朋友一起看看吧
    2024-02-02
  • redis集群實(shí)現(xiàn)清理前綴相同的key

    redis集群實(shí)現(xiàn)清理前綴相同的key

    這篇文章主要介紹了redis集群實(shí)現(xiàn)清理前綴相同的key,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 詳解Redis中的雙鏈表結(jié)構(gòu)

    詳解Redis中的雙鏈表結(jié)構(gòu)

    這篇文章主要介紹了Redis中的雙鏈表結(jié)構(gòu),包括listNode結(jié)構(gòu)的API,需要的朋友可以參考下
    2015-08-08
  • Redis?key-value亂碼的解決

    Redis?key-value亂碼的解決

    本文主要介紹了Redis?key-value亂碼的解決,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Spring Boot 項(xiàng)目集成Redis的方式詳解

    Spring Boot 項(xiàng)目集成Redis的方式詳解

    這篇文章主要介紹了Spring Boot 項(xiàng)目集成Redis的方式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧,需要的朋友可以參考下
    2021-08-08
  • Redis IP地址的綁定的實(shí)現(xiàn)

    Redis IP地址的綁定的實(shí)現(xiàn)

    這篇文章主要介紹了Redis IP地址的綁定的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • redis分布式鎖的go-redis實(shí)現(xiàn)方法詳解

    redis分布式鎖的go-redis實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了redis分布式鎖的go-redis實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì)對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Windows環(huán)境部署Redis集群

    Windows環(huán)境部署Redis集群

    這篇文章主要為大家詳細(xì)介紹了Windows環(huán)境部署Redis集群的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • 基于redis集群設(shè)置密碼的實(shí)例

    基于redis集群設(shè)置密碼的實(shí)例

    今天小編就為大家分享一篇基于redis集群設(shè)置密碼的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • Redis 出現(xiàn)錯(cuò)誤1067的解決辦法

    Redis 出現(xiàn)錯(cuò)誤1067的解決辦法

    這篇文章主要介紹了Redis 出現(xiàn)錯(cuò)誤1067的解決辦法的相關(guān)資料,Redis 錯(cuò)誤1067:進(jìn)程意外終止,Redis不能啟動(dòng),Redis啟動(dòng)不了,需要的朋友可以參考下
    2017-07-07

最新評(píng)論