冪等性的實現方案


我們實際系統中有很多操作,是不管做多少次,都應該產生一樣的效果或返回一樣的結果的。

冪等性的概念

冪等(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.狀態機冪等

在設計單據相關的業務,或者是任務相關的業務,肯定會涉及到狀態機(狀態變更圖),就是業務單據上面有個狀態,狀態在不同的情況下會發生變更,一般情況下存在有限狀態機,這時候如果狀態機已經處於下一個狀態,卻來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。 

要注意的是,訂單等單據類業務,存在很長的狀態流轉,一定要深刻理解狀態機,對業務系統設計能力提高有很大幫助。

總結

冪等性應該是一個合格程序員的基因,在設計系統的時候一定要考慮進去,尤其是像支付寶、銀行、互聯網金融公司等涉及的都是錢的系統,既要高效,也要准確,所以不能出現多扣款、多打款等問題,不然這樣會很難處理,用戶體驗也不會好。

 

"青春不能一直是隔岸觀火。"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM