Java解決高併發下商品庫存更新

1、問題分析
先來就庫存超賣的問題做描述:通常電子商務網站都會遇到如團購、秒殺、特價之類的活動,而這樣的活動有一個共同的特色就是訪問量激增、上千甚至上萬人搶購一個商品。
然而,做爲活動商品,庫存確定是頗有限的,如何控制庫存不讓出現超買,以防止形成沒必要要的損失是衆多電子商務網站程序員頭疼的問題,這同時也是最基本的問題。
從技術方面剖析,不少人確定會想到事務,可是事務是控制庫存超賣的必要條件,但不是充分必要條件。mysql

正常狀況下咱們會這樣寫:
//得到商品庫存
select counts from goods where id=?
//對比庫存和下單時購買商品數量
if(counts<buyAmount){
    //庫存不足...
}else{
    //業務代碼...更新庫存..
}
大多數人都會這麼寫,看似問題不大,其實隱藏着巨大的漏洞:
數據庫的訪問其實就是對磁盤文件的訪問,數據庫中的表其實就是保存在磁盤上的一個個文件,甚至一個文件包含了多張表。例如因爲高併發,當前有多個用戶進入到了這個事務中,
這個時候會產生一個共享鎖,因此在select的時候,這多個用戶查到的庫存數量都是N個,同時還要注意,mysql innodb查到的結果是有版本控制的,
再其餘用戶更新沒有commit以前(也就是沒有產生新版本以前),當前用戶查到的結果依然是N;
而後是update,假如這多個用戶同時到達update這裏,這個時候update更新語句會把併發串行化,也就是給同時到達這裏的多個用戶排個序,一個一個執行,並生成排他鎖,
在當前這個update語句commit以前,其餘用戶等待執行,commit後,生成新的版本;這樣執行完後,庫存或許爲負數了。程序員

改進版:
把上面的代碼稍微改動一下,能夠避免這樣狀況發生
//更新庫存
update goods set counts=counts-buyAmount where  id=?
//獲取當前商品庫存
select counts from goods where id=?
//判斷庫存是否爲負數
if(counts<0){
    //回滾事務
}else{
    //業務代碼...更新庫存..
}sql

可是==========================
在高併發的狀況下,確定不能如此高頻率的去讀寫數據庫,會嚴重形成性能問題的
必須使用緩存,將商品放入緩存中,並使用鎖來處理其併發狀況。當接到用戶提交訂單的狀況下,先將商品數量遞減(加鎖/解鎖)後再進行其餘方面的處理,處理失敗在將數據遞增1(加鎖/解鎖),
不然表示交易成功。當商品數量遞減到0時,表示商品秒殺完畢,拒絕其餘用戶的請求。數據庫

相關文章
相關標籤/搜索