比如商品 goods_id = 1
的商品庫存有10
件。100
個用戶同時購買,正常情況下只有10
個用戶能買到,其它用戶提示庫存不足。
方案一:
set autocommit = 0; begin; select sku from goods_sku where goods_id = 1; if(sku > 0){ update goods_sku set sku = sku - 1 where goods_id = 1; }else{ throw NoEnoughGoodsException(); } commit;
這種方案無法防止超賣。比如庫存還有1
,兩個線程同時查到庫存都為1
后,做更新操作,這時庫存就為-1
了。
方案二
set autocommit = 0; begin; select sku from goods_sku where goods_id = 1 for update; if(sku > 0){ update goods_sku set sku = sku - 1 where goods_id = 1; }else{ throw NoEnoughGoodsException(); } commit;
這種方案防止了超賣,但是每次 select 都加了排他鎖,阻塞其它線程讀。
方案三
set autocommit = 0; begin; int affected = update goods_sku set sku = sku - 1 where goods_id = 1 and sku > 0; if( affected != 1){ throw NoEnoughGoodsException(); } commit;
這種方案解決了上面的問題,但是對數(shù)據(jù)庫的壓力還是很大。
方案四
使用Redis等NoSQL數(shù)據(jù)庫。