首頁 >後端開發 >php教程 >防訂單重複提交策略方法

防訂單重複提交策略方法

藏色散人
藏色散人轉載
2019-05-14 09:17:275713瀏覽

背景

在業務開發中,我們常常會面對防止重複請求的問題。當服務端對於請求的回應涉及資料的修改,或狀態的變更時,可能會造成極大的危害。重複請求的後果在交易系統、售後維權,以及支付系統中尤其嚴重。

前台操作的抖動,快速操作,網路通訊或後端回應慢,都會增加後端重複處理的機率。前台操作去抖動和防快速操作的措施,我們首先會想到在前端做一層控制。當前端觸發操作時,或彈出確認介面,或disable入口並倒數計時等等,此處不細表。但前端的限制僅能解決少部分問題,且不夠徹底,後端自有的防重複處理措施必不可少,義不容辭。

在介面實作中,我們常要求介面要滿足冪等性,來確保多次重複請求時只有一次有效。

查詢類別的介面幾乎總是冪等的,但在包含諸如資料插入,多模組資料更新時,達到冪等性會比較難,尤其是高並發時的冪等性要求。例如第三方支付前台回調和後台回調,第三方支付批量回調,慢性能業務邏輯(如用戶提交退款申請,商家同意退貨/退款等)或慢網絡環境時,是重複處理的高發場景。

嘗試

這裡針對「使用者提交退款申請」的例子,說明一下嘗試過的防重複處理方法的效果。後端防重複處理的方式,我們先後嘗試了三種:

(1)基於DB中退款訂單狀態的驗證

這種方式簡單直觀,從DB查詢出來的退款詳情(包括狀態)往往還可以用在後續邏輯中,沒有花額外的工作專門處理重複請求的問題。

這種查詢狀態後進行驗證的邏輯,從程式碼上線後就一直存在於所有含狀態的業務邏輯處理中,必不可少。但對於防重複處理效果並不好:在前端添加防重複提交前,每週平均在25筆;前端優化後,每週降到7筆。這個數量佔總退款申請數的3%%,一個仍無法接受的比例。

理論上,任意次請求只要在資料狀態更新之前都完成了查詢操作,則業務邏輯的重複處理就會發生。如下圖所示。最佳化的方向是減少查詢到更新之間業務處理時間,可降低空檔期的同時影響。極致情況下如果查詢和更新變成了原子操作,則不存在我們目前的問題。

防訂單重複提交策略方法

(2)基於快取資料狀態的驗證

Redis儲存查詢輕量快速。在request進來的時候,可以先記錄在快取中。後續進來的request每次進行驗證。整個流程處理完成,清除快取。以退款為範例:

  • I. 每次退款發起申請,讀取快取中是否有以orderId為key的值
  • II. 沒有,則往快取中寫入以orderId為key的value
  • III.有,則表示有該訂單的退款正在進行中。
  • IV. 操作完清緩存,或是快取存值的時候設定生命週期

#與1)的發放相比,資料庫換成回應更快的快取。但仍然不是原子操作。插入和讀取快取還是有時間間隔。在極致的情況下還是有重複操作的情況。此方法優化後,每週1筆重複操作。

防訂單重複提交策略方法

(3)利用唯一索引機制的驗證

需要原子性操作,想到了資料庫的唯一索引。新建一個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 '业务ID',
`status` int(11) NOT NULL DEFAULT '0' COMMENT '锁状态',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Trade锁机制';

● 每次request進來則往表裡面插入資料:

成功,则可以继续操作(相当于获取锁);
失败,则说明有操作在进行。

● 操作完成後,刪除此筆記錄。 (相當於釋放鎖)。

目前已經上線,等待下週的數據統計。

防訂單重複提交策略方法

(4)基於快取的計數器驗證

由於資料庫的操作比較消耗性能,了解到redis的計數器也是原子性操作。果斷採用計數器。既可以提高效能,還不用存儲,而且能提升qps的峰值。

還是以訂單退款為例子:

● 每次request進來則新建一個以orderId為key的計數器,然後+1。

如果>1(不能获得锁): 说明有操作在进行,删除。
如果=1(获得锁): 可以操作。

● 操作結束(刪除鎖定):刪除這個計數器。

要了解计数器,可以参考:http://www.redis.cn/commands/incr.html

防訂單重複提交策略方法

#

总结:

PHP语言自身没有提供进程互斥和锁定机制。因此才有了我们上面的尝试。网上也有文件锁机制,但是考虑到我们的分布式部署,建议还是用缓存。在大并发的情况下,程序各种情况的发生。特别是涉及到金额操作,不能有一分一毫的差距。所以在大并发要互斥的情况下可以考虑3、4两种方案。

爱迪生尝试了1600多种材料选择了钨丝发明了灯泡,实践出真知。遇到问题,和问题斗争,最后解决问题是一个最大提升自我的过程,不但加宽自己的知识广度,更加深了自己的技能深度。达到目标之后的成就感更是不言而喻。


以上是防訂單重複提交策略方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除