通俗的說,使用者在系統中有操作,不管重複多少次,都應該產生一樣的效果或返回一樣的結果的。
冪等性的概念
冪等(Idempotent)是一個數學與計算機學的概念,常見於抽象代數中。
f(n) = 1^n // 無論n等於多少,f(n)永遠值等於1
在程式設計中,一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。冪等函式或冪等方法是指可以使用相同引數重複執行,並能獲得相同結果的函式/方法。這些函式/方法不會影響系統狀態,因此不用擔心重複執行會對系統造成改變。例如:
1.前端重複提交選中的資料,後臺也只會產生對應這個資料的一個反應結果。
2.使用者發起一筆付款請求,就應該只扣使用者一次錢,即使遇到網路重發或系統bug重發請求,也應該之扣一次錢。
3.傳送驗證短息也應該只發一次,同樣的驗證簡訊不應該傳送多次。
4.建立業務訂單,一個業務請求只能建立一個業務訂單,建立多個就會出大問題。
這些等等很多的業務邏輯都需要冪等的特性來支援。
簡單來理解就是,冪等就是一個操作,這個操作不管執行多少次,產生的效果和返回的結果都是一樣的。比如說有一個getOne()函式,無論執行這個函式多少次,它返回的都是1,這時就可以說它是一個冪等函式。
冪等性的技術方案
1.查詢操作
查詢一次和查詢多次,在資料不變的情況下,查詢結果都是一樣的,select是天然的冪等操作。
2.刪除操作
刪除操作也是冪等的,刪除一次和刪除多次都是把資料刪除。
3.建立唯一索引,防止新增髒資料
當表存在唯一索引,併發時新增重複記錄就會報錯,那麼這時候就查詢已存在的記錄並返回即可。
4.Token機制,防止頁面重複提交
頁面資料只能夠提交一次,但是由於出現重複點選或者網路重發或Nginx重發等情況導致資料被重複提交的情況下,可以採用Token+Redis(Redis是單執行緒的,處理需要排隊)的解決方案。處理的流程是,在資料提交前要向伺服器申請帶有有效時間的Token,然後Token放到Redis或JVM記憶體中,當資料正式提交到後臺要校驗Token並刪除Token。
5.悲觀鎖
獲取資料的時候加鎖獲取:
select * from table where id = 'xxx' for update;
要注意的是,id欄位一定要是主鍵或者唯一索引,否則會導致鎖表。
悲觀鎖的使用一般伴隨事務一起使用,資料鎖定事件可能會很長,要根據實際情況慎用。
6.樂觀鎖
樂觀鎖只是在更新資料的那一刻鎖表,其他時間不鎖表,所以相對於悲觀鎖效率更高。
樂觀鎖的實現方式多種多樣,可以通過version或者其他狀態條件。
7.分散式鎖
還是拿插入資料的例子,如果是分散式系統,構建全域性唯一索引比較困難,例如唯一性的欄位無法確定。那麼這時候就可以引入分散式鎖,通過第三方的系統(Redis或Zookeeper),在業務系統插入資料或更新資料,獲取分散式鎖,然後做操作,之後再釋放鎖。這樣其實是把多執行緒併發鎖的思路引入了多個系統,也就是分散式系統中的解決思路。
要注意的是,某個長流程處理過程要求不能併發執行,可以在流程執行之前根據某個標誌(使用者ID+字尾等)獲取分散式鎖,其他流程執行時獲取鎖就會失敗,也就是同一時間該流程只能有一個能執行成功,執行完成後,釋放分散式鎖(分散式鎖需要第三方系統提供))。
8.select+insert
對於一些併發不高的後臺系統,或者一些任務Job,為了支援冪等,支援重複執行,簡單的處理方法是先查詢下一些關鍵資料,判斷是否已經執行過,然後再進行業務處理就可以了。但是要注意的是核心高併發流程不要用這種方法,因為效率較低。
9.狀態機冪等
在設計單據相關的業務,或者是任務相關的業務,肯定會涉及到狀態機(狀態變更圖),就是業務單據上面有個狀態,狀態在不同的情況下會發生變更,一般情況下存在有限狀態機,這時候如果狀態機已經處於下一個狀態,卻來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。
要注意的是,訂單等單據類業務,存在很長的狀態流轉,一定要深刻理解狀態機,對業務系統設計能力提高有很大幫助。
總結
冪等性應該是一個合格程式設計師的基因,在設計系統的時候一定要考慮進去,尤其是像支付寶、銀行、網際網路金融公司等涉及的都是錢的系統,既要高效,也要準確,所以不能出現多扣款、多打款等問題,不然這樣會很難處理,使用者體驗也不會好。
首發自我的面試總結手記
www.kancloud.cn/martist/be_new_fri...
本作品採用《CC 協議》,轉載必須註明作者和本文連結