我們知道資料庫處理sql是一條條處理的,假設購買商品的流程是這樣的:
sql1:查詢商品庫存if(庫存數量 > 0){ //產生訂單... sql2:庫存-1}
當沒有並發時,上面的流程看起來是如此完美,假設同時兩個人下單,而庫存只有1個了,在sql1階段兩個人查詢到的庫存都是>0的,於是最終都執行了sql2,庫存最後變為-1,超售了,要麼補庫存,要麼等使用者投訴吧。
解決這個問題比較好的方法是什麼呢?
回複內容:
我們知道資料庫處理sql是一條條處理的,假設購買商品的流程是這樣的:
sql1:查詢商品庫存if(庫存數量 > 0){ //產生訂單... sql2:庫存-1}
當沒有並發時,上面的流程看起來是如此完美,假設同時兩個人下單,而庫存只有1個了,在sql1階段兩個人查詢到的庫存都是>0的,於是最終都執行了sql2,庫存最後變為-1,超售了,要麼補庫存,要麼等使用者投訴吧。
解決這個問題比較好的方法是什麼呢?
庫存欄位改成unsigned int。
這樣的話不會<0,頂多就SQL執行出錯。
也可以用memcached/redis來緩衝結果,從緩衝中查詢。
1.MySQL資料庫加鎖,樂觀鎖 和 悲觀鎖
2.用隊列,排隊逐一去產生訂單
庫存緩衝一般都是 有的吧,如果有並發需求。 還有就是加鎖,或者放到隊列執行sql。
有種很笨的方法,已樓主的假設為例:
sql1:查詢商品庫存 (假設查出的庫存為10)if(10 > 0){ //產生訂單... sql2: 10 - 1 (此時庫存為9了) //再校正庫存 sql3: 查詢商品庫存 == 9 (如果此時有並發情況,那查出來的庫存可能為8、7等,這時拋出異常,交易回復,該筆訂單無效。)}
處理高並發一般來說都會用到Redis,使用Redis的list資料結構(高並發當然要非同步隊列咯)來儲存請求過來的訂單資訊,然後啟用redis的事務機制(見:http://redis.io/topics/transactions),加入指定key的list前,判斷該list等長度是否超過redis中儲存的指定商品庫存值,如果超過則不操作,如果不超過,則進行插入,插入之後,再次判斷該list的長度是否超過redis中儲存的指定商品的庫存值,如果超過則復原,否則提交。
想問一下,並發量大的情況下,是怎麼做的呢?
1.用事務保持操作原子性,
2.在修改庫存的時候先用select...for update把資料鎖好
update table set n = n-x where n >= x
不需要事務,直接擷取affected row count來判斷是否扣成功,後到的那一條因為n=0,affected row肯定為0