您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(guān)如何防訂單重復(fù)提交策略的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。
在業(yè)務(wù)開發(fā)中,我們常會面對防止重復(fù)請求的問題。當(dāng)服務(wù)端對于請求的響應(yīng)涉及數(shù)據(jù)的修改,或狀態(tài)的變更時,可能會造成極大的危害。重復(fù)請求的后果在交易系統(tǒng)、售后維權(quán),以及支付系統(tǒng)中尤其嚴(yán)重。
前臺操作的抖動,快速操作,網(wǎng)絡(luò)通信或者后端響應(yīng)慢,都會增加后端重復(fù)處理的概率。前臺操作去抖動和防快速操作的措施,我們首先會想到在前端做一層控制。當(dāng)前端觸發(fā)操作時,或彈出確認(rèn)界面,或disable入口并倒計時等等,此處不細表。但前端的限制僅能解決少部分問題,且不夠徹底,后端自有的防重復(fù)處理措施必不可少,義不容辭。
在接口實現(xiàn)中,我們常要求接口要滿足冪等性,來保證多次重復(fù)請求時只有一次有效。
查詢類的接口幾乎總是冪等的,但在包含諸如數(shù)據(jù)插入,多模塊數(shù)據(jù)更新時,達到冪等性會比較難,尤其是高并發(fā)時的冪等性要求。比如第三方支付前臺回調(diào)和后臺回調(diào),第三方支付批量回調(diào),慢性能業(yè)務(wù)邏輯(如用戶提交退款申請,商家同意退貨/退款等)或慢網(wǎng)絡(luò)環(huán)境時,是重復(fù)處理的高發(fā)場景。
這里針對“用戶提交退款申請”的例子,說明一下嘗試過的防重復(fù)處理方法的效果。后端防重復(fù)處理的方式,我們先后嘗試了三種:
這種方式簡單直觀,從DB查詢出來的退款詳情(包括狀態(tài))往往還可以用在后續(xù)邏輯中,沒有花額外的工作專門應(yīng)對重復(fù)請求的問題。
這種查詢狀態(tài)后進行驗證的邏輯,從代碼上線后就一直存在于所有含狀態(tài)的業(yè)務(wù)邏輯處理中,必不可少。但對于防重復(fù)處理效果并不好:在前端添加防重復(fù)提交前,每周平均在25筆;前端優(yōu)化后,每周降到7筆。這個數(shù)量占總退款申請數(shù)的3%%,一個仍然無法接受的比例。
理論上,任意次請求只要在數(shù)據(jù)狀態(tài)更新之前都完成了查詢操作,則業(yè)務(wù)邏輯的重復(fù)處理就會發(fā)生。如下圖所示。優(yōu)化的方向是減少查詢到更新之間業(yè)務(wù)處理時間,可降低空檔期的并發(fā)影響。極致情況下如果查詢和更新變成了原子操作,則就不存在我們當(dāng)前的問題。
Redis存儲查詢輕量快速。在request進來的時候,可以先記錄在緩存中。后續(xù)進來的request每次進行驗證。整個流程處理完成,清除緩存。以退款為例子:
與1)的發(fā)放相比,數(shù)據(jù)庫換成響應(yīng)更快的緩存。但是仍然不是原子操作。插入和讀取緩存還是有時間間隔。在極致的情況下還是存在重復(fù)操作的情況。此方法優(yōu)化后,每周1筆重復(fù)操作。
需要原子性操作,想到了數(shù)據(jù)庫的唯一索引。新建一個TradeLock表:
CREATE TABLE `TradeLock` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `type` int(11) NOT NULL COMMENT '鎖類型', `lockId` int(11) NOT NULL DEFAULT '0' COMMENT '業(yè)務(wù)ID', `status` int(11) NOT NULL DEFAULT '0' COMMENT '鎖狀態(tài)', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Trade鎖機制';
● 每次request進來則往表里面插入數(shù)據(jù):
成功,則可以繼續(xù)操作(相當(dāng)于獲取鎖); 失敗,則說明有操作在進行。
● 操作完成后,刪除此條記錄。(相當(dāng)于釋放鎖)。
目前已經(jīng)上線,等待下周的數(shù)據(jù)統(tǒng)計。
由于數(shù)據(jù)庫的操作比較消耗性能,了解到redis的計數(shù)器也是原子性操作。果斷采用計數(shù)器。既可以提高性能,還不用存儲,而且能提升qps的峰值。
還是以訂單退款為例子:
● 每次request進來則新建一個以orderId為key的計數(shù)器,然后+1。
如果>1(不能獲得鎖): 說明有操作在進行,刪除。 如果=1(獲得鎖): 可以操作。
● 操作結(jié)束(刪除鎖):刪除這個計數(shù)器。
要了解計數(shù)器,可以參考:http://www.redis.cn/commands/incr.html
PHP語言自身沒有提供進程互斥和鎖定機制。因此才有了我們上面的嘗試。網(wǎng)上也有文件鎖機制,但是考慮到我們的分布式部署,建議還是用緩存。在大并發(fā)的情況下,程序各種情況的發(fā)生。特別是涉及到金額操作,不能有一分一毫的差距。所以在大并發(fā)要互斥的情況下可以考慮3、4兩種方案。
愛迪生嘗試了1600多種材料選擇了鎢絲發(fā)明了燈泡,實踐出真知。遇到問題,和問題斗爭,最后解決問題是一個最大提升自我的過程,不但加寬自己的知識廣度,更加深了自己的技能深度。達到目標(biāo)之后的成就感更是不言而喻。
感謝各位的閱讀!關(guān)于如何防訂單重復(fù)提交策略就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。