比如商品 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;
这种方案解决了上面的问题,但是对数据库的压力还是很大。
方案四
使用Redis等NoSQL数据库。