1、問題描述前端
涉及到的功能是一個表單頁面添加數據,後端接口的功能就是往數據庫中寫入表單的數據,系統屬於後臺系統,但由於跟錢有關,添加的數據要保證同一次填寫的數據只能入一次庫。java
測試很負責任,在測試保存時開啓所有能量狂點保存按鈕,結果出現了多條同樣的數據,問題由此產生mysql
分析問題,緣由有兩個:1. 表單沒有作防重複提交,致使出現多個請求,2. 後端保存接口併發時沒法判斷同一表單的請求。redis
2、過程sql
Round 1:在表單提交後,把按鈕置爲disabled
FAIL:這種解決方案太依賴前端,而且沒法保證刷新時重複提交表單數據庫
Round 2:作表單防重複提交,在頁面打開時,在session和頁面中設置token,表單提交時判斷token,並刪除session中的token
FAIL:目前的項目中沒有先例,要解決防重複提交的問題應考慮整個項目的處理機制,不該特殊化某一功能,對後期維護很差,同時多個頁面的token在session中也須要有不一樣的key,而項目部署時有多個實例,用session實現須要太多的前提條件後端
Round 3:既然不經過防重複提交解決,那就解決併發問題吧,java中提供了java自己就提供了synchornized和Lock,不只如此,mysql也提供了get_lock,release_lock方法,用以獲取和釋放鎖
FAIL:在業務代碼中特殊處理了這一小部分代碼會使代碼總體風格變味,同時也同樣避免不了去重的邏輯判斷(好比經過填寫的某些關鍵信息),因此也不是個好辦法session
Round 4:mysql的unique_key自然提供了防重複的功能,最終選中unique_key來解決併發重複寫入的問題。但在解決過程當中要對程序作些改造,首先表單數據在保存時會有流水號信息,而且流水號在數據庫中設置了unique_key,原先的程序流水信息是在信息保存時生成,改造後,在添加頁面打開時就生成流水信息,並放置在表單的隱藏域內,表單提交後,只是原樣保存提交過來的流水號,這樣即便有多個一樣的表單提交,最終也只會成功一筆,其餘均會失敗。
SUCC:這麼作的不足是流水號可能會被浪費,屢次打開表單不提交時,流水號也會生成。另外一不足,若是經過非正常手段,變動了流水號再提交,那也沒辦法阻止,由於對業務來講,不一樣流水號就被認爲了避免同的業務。併發
3、總結測試
解決這類併發問題,能夠有如下途徑:
1. 找到業務的惟一約束,使用mysql的unique_key解決 2. 根據業務,若是不能找到惟一約束,那就用鎖解決吧,能夠選擇java的鎖,mysql的鎖,或者redis實現