iT邦幫忙

0

利用primary key防止超賣

各位好
小弟目前碰到可能超賣的問題
上網找了不少資料
但由於mysql資料庫版本非常舊還有一些因素
redis不給裝
也不能用unsigned型態 和 innodb
這讓我不能走transaction這方向

所以小弟想到一個方法
就是假設現在賣A商品限量30個
我先開一個table item_detail
把這30個都塞進去
用一個primary key區別開
當使用者要下訂的時候
會直接從這資料表找沒有賣出(狀態有欄位會記)的商品
並把他的primary key插入到 另一個table order的同樣也是primary key的欄位中
由於mysql會檢查 primary key的重複性
當有人同時操作時
一定不會發生同時下訂同一商品的狀況
而且也不會超過item_detail的總數

但由於還沒實際開放
所以先上來問一下各位大大
小弟這想法會碰到什麼問題嗎?

看更多先前的討論...收起先前的討論...
淺水員 iT邦研究生 4 級 ‧ 2020-10-15 11:13:57 檢舉
沒有 transaction ,那可以用 lock table 嗎?
slime iT邦大師 1 級 ‧ 2020-10-15 11:35:41 檢舉
大方向沒問題, 細節上:
1. 放入購物車算不算買了? 還是要結帳才算買了?
2. 若有退貨的判斷?
st474ddr iT邦新手 5 級 ‧ 2020-10-15 13:18:01 檢舉
@淺水員大大
是指表鎖對吧
lock table ‘table_name’ write; 這種?
這我查資料也都有查到 但我沒試過
那這樣我可能要改一下資料表設計
單一商品是記庫存量 用扣庫存量的方式

@slime大大
1.沒有購物車
流程就是 (有兩個頁面)
A頁面:填入基本資料 勾選要買的東西 送出表單
B頁面:會印出他選擇的東西和基本資料 讓他確認
按下確認就算預定

2.沒有退貨
其實我沒講清楚
是預購的網頁
淺水員 iT邦研究生 4 級 ‧ 2020-10-16 14:13:38 檢舉
這幾天忙沒回到,不過看來已經解決了
st474ddr iT邦新手 5 級 ‧ 2020-10-16 17:46:05 檢舉
@潛水員 大大
解決了 感謝大大
3
浩瀚星空
iT邦超人 1 級 ‧ 2020-10-15 12:05:12
最佳解答

你的想法不錯,只是做法不好。
如果當有一堆商品且數量都高達1000以上的情況下,這樣做就不太合適。

以前的購物車做法給你參考。

商品欄位會區分「庫存量」「等待售出庫存量」

在下訂商品的當下,就會產生訂單。並將「庫存量」扣除到「等待售出庫存量」
訂單狀態會是在等待確認的情況。「等待確認單」最多只會等15分~1個小時(時間當時設計給業主自行設定)。
當一個小時超過還沒進入「確認」的情況下。
會將該單列為取消單。並將原本在「等待售出庫存量」轉回「庫存量」

在同時間同時下訂的情況下。基本當下會鎖表處理。

看更多先前的回應...收起先前的回應...
st474ddr iT邦新手 5 級 ‧ 2020-10-15 13:34:06 檢舉

感謝大大回覆
我嘗試看看鎖表好了

可是如果我用寫鎖
這樣是不是會導致其他使用者在抓資料的時候卡住
目前商品的table就會變成
欄位:商品編號,商品名稱,價格,庫存數,等待售出庫存量
當第一個連線進來時
會鎖住這個table
同時剛好進來購買頁面的資料是否就會抓不到?
(剛開始有抓這個table來秀相關的資料)

鎖表只在「庫存量」「等待售出庫存量」兩者之間轉換時處理。
時間不會超過1秒,誰叫你一直鎖的。想操爆你的主機啊。

鎖表之後,連查尋都不行的。
鎖表的目的只是為了不要發生讀取錯誤的庫存。
鎖表你要想像所有在用任何這張表的操作。都會變成等待。
雖然叫你用鎖表,但不要隨便鎖表。

其實現在有點害怕教你用鎖表。

st474ddr iT邦新手 5 級 ‧ 2020-10-15 14:59:48 檢舉

哈哈
我前面沒講清楚
抱歉造成大大誤解
我的第一個連線是第一個使用者下單

據我目前的理解
就是送 update query前lock table
執行完unlock

所以我原本的意思是
當第一個使用者下單時
扣庫存那一時刻已經鎖表狀態中(1秒內)了
另一個使用者剛好進頁面讀取資料
這樣是不是就會抓不到了

他會等待到超時或是unlock。不會發生抓不到。(雖然超時就會了沒錯)
我上面說不要超過1秒內。但其實光等待0.5秒就很容易發生可怕的事了。如果人數多的話會更是可怕。

1
japhenchen
iT邦高手 1 級 ‧ 2020-10-15 11:32:41

每開啟這個限量商品就從資料庫中取得限量的水號,資料庫同時也標記這筆記錄已被取走,記錄取用時間並更新回資料庫,這時並不代表交易已經完成,只是在取得交易流水號的動作而已,在完成交易後再更新這筆交易編號的狀態為已售出,如果用戶中途離線或交易過久,你的後端再把標記時間超過10分鐘但尚未標記完成交易的記錄收回並重新發給客戶端使用(放SESSION),直到所有水號都已經更動狀態成售出為止

這就不會發生超量的問題

有人會說要是有人故意多開瀏覽器來佔水號,那你不會做同IP只能取一次啊?或是讓用戶必需加入會員,每一會員只能同時開一個..

這只是我的做法,別人或有別的想法,你參考看看

st474ddr iT邦新手 5 級 ‧ 2020-10-15 13:26:44 檢舉

感謝大大回覆
關於大大的

每開啟這個限量商品就從資料庫中取得限量的水號,資料庫同時也標記這筆記錄已被取走

請問我用Primary key 插入的方式
跟您的取得限量水號的流程 是類似的
小弟不才 看不出您如何避免併發取得水號狀況

透過user defined function取得水號,在function傳回可用編號前,update table set status = 'taken' where id = @returnid

st474ddr iT邦新手 5 級 ‧ 2020-10-15 15:09:07 檢舉

我了解大大您的意思了
我覺得我的作法有點類似
現在就考慮要以這種作法還是用鎖表的方式

0
ckp6250
iT邦研究生 3 級 ‧ 2020-10-15 20:03:44

我怎麼覺得,應該先解決 MYSQL 問題,也不能用unsigned型態 和 innodb,這基本東西不處理掉,沙灘上蓋大樓,安穏嗎?

我並不主張追求太太太新的東西,但對於太太太舊的古董則十分排斥,有些時候,新工具一行或一分鐘能解決的問題,老古董可能得花好幾十倍的心力。

st474ddr iT邦新手 5 級 ‧ 2020-10-16 13:08:36 檢舉

大大感謝您的回覆
因為已經有太多程式和資料的堆疊
所以暫時都還沒有切換
其實目前上級是已經有在計畫慢慢轉換了XDD~

我要發表回答

立即登入回答