記一次遇到由於重復提交導致的問題
阿新 • • 發佈:2018-03-24
就是 按鈕 說了 ... 修改密碼 into 修改 方案 batis 方法就是select看是否存在記錄。
- 需求
- 問題
- 解決和復盤
這是新手期間第一個上線功能搞出的bug,同時也明白了一個道理:1. 線上環境總是復雜的,不可預知的,一定要做好各種準備; 2. 重要的功能要做放重復提交;3. 基礎要打打牢。
需求
需求其實很簡單,就是一個修改密碼的入口,用戶輸入提交表單後,如果之前沒有設置過密碼,則設置,否則修改。然後驗證密碼的邏輯就不用說了,查出表裏的密碼,然後對比。
項目是ibatis+oralce,之前DAO的邏輯如下
...
if (isNotExists()){
insertPassword;
} else {
updatePassword;
}
而isNotExists()
public Map queryPassword(Map param) throws DAOException {
return sqlMapClientTemplate.queryForObject(param);
}
問題
某一天中午突然來了告警日誌,發現在queryPassword
這個方法這裏拋出了too many results
異常,很顯然,本應該每個用戶只存在一條記錄的,查出了多條。讓運維同事查出數據,果然有一個用戶,存在3條記錄。查看原代碼邏輯,那很顯然是在判斷是否存在記錄這裏同時來了三個請求,然後三個線程都發現該用戶對應的記錄是空的,於是同時插入了三條記錄。(後來通過電話回訪用戶,說當時手機頁面卡了,於是瞎點一通,可能頁面卡了,等反應後一下子發出去多條請求。)
解決和復盤
- 首先線上問題緊急的解決方案就是刪掉記錄就OK了。
然後這種
if ... then...
的用法是非常危險的,一來一回,在並發情況下就出現問題了。當時想著改個密碼對同一個用戶哪兒來的多並發,先就這樣幹吧,事實證明線上環境的復雜是永遠超出自己預知的。對於這種場景,最好使用oralce提供的merge方法,把事務處理交給oracle,出錯的概率更小一些。merge into pwd_table t using dual on (t.id = #id#) when matched then update pwd_table t set t.pwd = #pwd# where
- 做防重復提交限制,考慮到用戶的群體,最簡單的是在用戶提交請求得到響應之前,按鈕置為diabled狀態
基礎一定要打好,當時就是對merge了解不深沒有使用,要多學習PL/SQL的東西
記一次遇到由於重復提交導致的問題