一、摘要
對於投入運營的軟件系統,最近小編在巡檢項目數據庫的時候,發現某些表存在不少的重複數據,對於這樣的髒數據,初步分析大致的來源有以下可能:
- 1.由於用戶誤操作,多次點擊表單提交按鈕
- 2.由於網速等原因造成頁面卡頓,用戶重複刷新提交頁面
- 3.黑客或惡意用戶使用 postman 等網絡工具,重複惡意提交表單
這些情況都可能會導致表單重複提交,造成數據重複,比如訂單表,重複提交訂單數據所造成的問題,可能不僅僅是數據上的混亂,也會造成業務混亂。
那麼問題來了,我們該如何防止用戶重複提交數據呢?
方案實踐如下!
二、方案實踐
下面我們以防止重複提交訂單爲例,向大家介紹最簡單的、成本最低的解決辦法。
我們先來看一張圖,這張圖就是本次方案的核心流程圖。
實現的邏輯,流程如下:
- 1.當用戶進入訂單提交界面的時候,調用後端獲取請求唯一ID,並將唯一ID值埋點在頁面裏面
- 2.當用戶點擊提交按鈕時,後端檢查這個唯一ID是否用過,如果沒有用過,繼續後續邏輯;如果用過,就提示重複提交
- 3.最關鍵的一步操作,就是把這個唯一ID 存入業務表中,同時設置這個字段爲唯一索引類型,從數據庫層面做防止重複提交
防止重複提交的大體思路如上,實踐代碼如下!
2.1、給數據庫表增加唯一鍵約束
以訂單表爲例,新增一個request_id
字段,並設置爲唯一約束,結構如下:
CREATE TABLE tb_order (
id bigint(20) unsigned NOT NULL,
order_no varchar(100) NOT NULL,
....
request_id varchar(36) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uniq_request_id (request_id) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2、編寫獲取請求唯一ID的接口
@RestController
@RequestMapping("api")
public class CommonController {
/**
* 獲取getRequestId
* @return
*/
@RequestMapping("getRequestId")
public ResResult getRequestId(){
String uuid = UUID.randomUUID().toString();
return ResResult.getSuccess(uuid);
}
}
2.3、業務提交的時候,檢查唯一ID
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 下單
* @param request
* @return
*/
@PostMapping(value = "order/confirm")
public ResResult confirm(@RequestBody OrderConfirmRequest request){
//調用訂單下單相關邏輯
if(StringUtils.isEmpty(request.getRequestId())){
return ResResult.getSysError("請求ID不能爲空!");
}
if(request.getRequestId().length() != 36){
return ResResult.getSysError("請求ID格式錯誤!");
}
//檢查當前請求唯一ID,是否已經存在,如果存在,再提交就是重複下單
Order source = orderService.queryByRequestId(request.getRequestId());
if(Objects.nonNull(source)){
return ResResult.getSysError("當前訂單已經提交成功,請勿重複提交");
}
orderService.confirm(request);
return ResResult.getSuccess();
}
}
如果是併發請求也不用擔心,因爲數據庫表已經設置了唯一索引,尤其只有一條有效數據會插入成功,可以防止重複的數據產生。
三、小結
對於下單流量不算高的系統,可以採用這種請求唯一ID
+數據表增加唯一索引約束
的方式,來防止接口重複提交!
雖然簡單粗暴,但是十分有效!
可能有的人會問,看上面的代碼生成請求唯一 ID 很簡單,爲啥不直接前端生成一個請求唯一ID
,然後提交呢?
之所以把獲取請求唯一ID
的生成規則放在後端,好處就是生成規則可以自己定義,也並不一定要用uuid
來生成,也可以用雪花算法,或者自己設計一套計算規則,保證當前業務提交時請求ID是唯一的,比如事先生成唯一的訂單號,作爲請求唯一ID,然後再提交,規則放在後端來生成,會更加靈活!