訂單重復問題已經是老生常談的問題了锅风,我們熟悉的淘寶購物流程兢卵,購物車-->生成訂單--》提交訂單--》訂單確認--》支付訂單--》備貨--》發(fā)貨蜈首,這里可能會有一些問題岗憋,例如肃晚,有人惡意或者無意的重復提交訂單,從而導致數據庫保存的訂單數量與實際不符仔戈,重復提交訂單導致用戶體驗不佳关串,甚至更為嚴重的是,用戶重復支付了同一個訂單监徘。所以悍缠,涉及到訂單,應該首先想清楚如何設計才能保證系統(tǒng)不會出現這些問題
如何防止訂單重復提交
首先說兩個我們購物時經常有過的體驗或者說購物網站的網頁提醒
- 你提交的動作過快耐量,請稍后嘗試
- 你的訂單已經超時飞蚓,請刷新頁面后重新提交
看到這些提示,說明該購物網站做了訂單提交的限制廊蜒,一方面是防止有人惡意無限制提交訂單趴拧,所以限制了一定時間內最大可操作次數,另一方面是為了保證訂單無重復提交山叮。那么這是怎么做到的呢著榴?
第一個應該比較簡單,限制某個時間內的最大操作次數只需要有一個計數器就可以屁倔,計數器可以用redis實現脑又,設置一個帶有有效時間的值作為計數器,如果值不存在則自動創(chuàng)建,超過某一個值就認為操作次數用完即可以實現问麸。
第二個可以使用token機制往衷,token即令牌,學過spring security的相信對這個詞不會陌生严卖。我們可以使用類似spring security的機制在頁面上生成一個token席舍,當提交訂單時,根據該token的有效時間和允許的使用次數來判斷訂單是否允許提交哮笆,從而規(guī)避重復提交的問題来颤。當然,有人會問稠肘,在高并發(fā)的情況下福铅,如果是判斷token有效之前有很多同一個用戶的提交線程過來(用戶正常使用一般不會出現這種情況,一般是壓力測試工具導致的)项阴,那么還是會重復提交本讥,所以,這里需要用到鎖機制鲁冯,訪問同一個用戶的token同一時間只能有一個線程,token使用之后失效了就會被清掉色查,之后的線程就找不到該token薯演,從而認為訂單不能提交。
訂單確認支付
如支付寶和微信等秧了,支付寶和微信本身是怎么限制訂單只能支付一次的呢跨扮?訂單怎么保證只會被支付一次呢?這個問題相對來說就簡單很多了验毡,同一訂單的狀態(tài)更新的SQL只需要帶上條件衡创,利用的是數據庫的行鎖。當然晶通,如果是分布式系統(tǒng)璃氢,這里涉及到的問題會更多。
update table item set item.status=:newstatus where item.id = :id and item.status = oldstatus.
對比案例
-
美團GTIS
主要看GTIS的流程圖以及想想其交易ID的作用狮辽,交易之前一也,后臺會返回一個交易ID給前端,前端在點擊交易按鈕時需將該交易ID和其他交易信息同時返回給后臺進行處理喉脖,通過全局的交易ID實現“該次交易的”冪等性
美團GTIS.png
參考資料和研究
- 分布式鎖的對比分析:非常好的一篇文章椰苟,包含下面的關鍵詞:可重入、阻塞树叽、公平舆蝴、排他、樂觀、悲觀洁仗、單點层皱、死鎖發(fā)生、連接池狀況京痢、實現種類奶甘、實現方式