互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
來自:https://blog.csdn.net/new_com/article/details/105568124
在商品購買的過程中,庫存的抵扣過程,一般操作如下:1、select根據(jù)商品id查詢商品的庫存。2、根據(jù)下單的數(shù)量,計算庫存是否足夠,如果存庫不足則拋出庫存不足的異常,如果庫存足夠,則減去扣除的庫存得到最新的庫存剩余值。//?根據(jù)商品id獲取商品剩余庫存
select?stock_remaing?from?stock_table?where?id=${goodsId};
//?操作庫存
//?比較庫存
if(stock_remaing????//?拋出庫存不足的異常
}
else{
??//?抵扣以后的庫存值
??int?new_stock=stock_remaing?-?quantity;
}
//?根據(jù)商品id設(shè)置計算后的庫存
update?stock_table??set?stock_remaing?=${new_stock}?id=${goodsId};
如果數(shù)據(jù)庫事務(wù)的隔離級別不是串行化(serializable),根據(jù)事務(wù)的特性,在并發(fā)修改的時候,可能會出現(xiàn)寫覆蓋的問題。假設(shè),商品的剩余庫存stock_remaing 為100,客戶A下單20,客戶B下單30,在并發(fā)扣庫存的時候,可能存在超賣。如果客戶A和客戶B同時獲取剩余庫存為100,則會出現(xiàn)事務(wù)后提交的值會覆蓋前一個客戶提交的值,有可能剩余的庫存是80或者70。為了在事務(wù)控制中,防止寫覆蓋,你會想到使用select for update的方式,將該商品的庫存鎖住,然后執(zhí)行余下的操作。以上,使用悲觀鎖方式,在分布式服務(wù)中,如果并發(fā)情況比較高的時候,扣減庫存的操作是串行操作,效率很低。最新面試題整理好了,點(diǎn)擊Java面試庫小程序在線刷題。在更新的時候,使用(CAS+版本號更新)+重試條件(重試次數(shù)或者重試時間限制)樂觀鎖的方式更新庫存。此時,如果,客戶A和客戶B同時讀取到庫存剩余100,在更新的時候,有一個操作會失敗。該種方式可以大大提高并發(fā)性,也可以保證數(shù)據(jù)的一致性;通過重試次數(shù)和重試時間的條件控制,可以防止過多的重試帶來的數(shù)據(jù)庫壓力。在抵扣庫存的時候,有的人提議不執(zhí)行select,計算,set三段式的操作,直接扣減的方式,并且對于扣減到小于零的情況作了判斷。偽代碼如下:update?stock_table?set?remaing_stock=remaing_stock-${quantity}?
where?id?=商品id
and?remaing_stock>${quantity};
在分布式服務(wù)調(diào)用中,因?yàn)榫W(wǎng)絡(luò)異常,獲取服務(wù)器異常,可能在微服務(wù)調(diào)用時,存在服務(wù)重試。例如,場景的網(wǎng)關(guān)超時,服務(wù)重試機(jī)制。此時,該種方式不滿足冪等性,而存在多扣的情況。例如,同一用戶扣減庫存時,服務(wù)重試,極端情況下,該用戶扣減庫存操作執(zhí)行多次,則就出現(xiàn)了商品超賣。由于沒有研究過redis源碼,對于這種方式參考了大牛的回復(fù),答案是可以使用redis的事務(wù)性扣減余額,但在CAS機(jī)制上比mysql沒有優(yōu)勢,高性能是因?yàn)槠鋬?nèi)存存儲的原因,帶來的副作用是數(shù)據(jù)有丟失風(fēng)險。推薦閱讀:
這些年我用過的 6個API 接口文檔平臺,真的好用
Redis 分布式鎖如何自動續(xù)期
互聯(lián)網(wǎng)初中高級大廠面試題(9個G)內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬并發(fā)、消息隊列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper、數(shù)據(jù)結(jié)構(gòu)、限流熔斷降級......等技術(shù)棧!
?戳閱讀原文領(lǐng)取!? ? ? ? ? ? ? ??? ??? ? ? ? ? ? ? ? ? ?朕已閱?