咱們來談下高併發和分佈式中的冪等處理

clipboard.png

咱們先來談下冪等的概念
抽象概念
冪等(idempotent、idempotence)是一個數學與計算機學概念,常見於抽象代數中。
複製代碼
在編程中,一個冪等操做的特色是其任意屢次執行所產生的影響均與一次執行的影響相同。冪等函數,或冪等方法,是指可使用相同參數重複執行,並能得到相同結果的函數。這些函數不會影響系統狀態,也不用擔憂重複執行會對系統形成改變。例如,「getUsername()和setTrue()」函數就是一個冪等函數。前端

用通俗的話講:就是針對一個操做,無論作多少次,產生效果或返回的結果都是同樣的nginx

舉幾個例子:
1.好比前端對同一表單數據的重複提交,後臺應該只會產生一個結果程序員

2.好比咱們發起一筆付款請求,應該只扣用戶帳戶一次錢,當遇到網絡重發或系統bug重發,也應該只扣一次錢redis

3.好比發送消息,也應該只發一次,一樣的短信若是屢次發給用戶,用戶會崩潰spring

4.好比建立業務訂單,一次業務請求只能建立一個,不能出現建立多個訂單
複製代碼
還有不少諸如此類的,這些邏輯都須要冪等的特性來支持。
實現冪等性的技術方案
查詢操做
查詢一次和查詢屢次,在數據不變的狀況下,查詢結果是同樣的,select是自然的冪等操做。
複製代碼
刪除操做
刪除操做也是冪等的,刪除一次和屢次刪除都是把數據刪除。(注意可能返回結果不同,刪除的數據不存在
,返回0,刪除的數據多條,返回結果多個)。
複製代碼
惟一索引,防止新增髒數據
拿資金帳戶和用戶帳戶來講,每一個用戶只能有一個資金帳戶,怎麼防止給用戶建立資金帳戶多個,那麼給資
金帳戶表中的用戶ID加惟一索引,在新增的時候只有一個能請求成功,剩下都會拋出惟一索引重複異常。比
org.springframework.dao.DuplicateKeyException,這時候再查詢一次就能夠了,數據存在,返回結果
複製代碼
token機制,防止頁面重複提交
要求:頁面的數據只能被點擊提交一次sql

發生緣由:因爲重複點擊或者網絡重發,或者nginx重發等狀況會致使數據被重複提交數據庫

解決辦法:編程

集羣環境:採用token加redis

單JVM環境:採用token加redis或token加jvm內存

處理流程:api

數據提交前要向服務的申請token,token放到redis或jvm內存,token有效時間

提交後後臺校驗token,同時刪除token,生成新的token返回

token特色:要申請,一次有效性,能夠限流安全

注意:redis要用刪除操做來判斷token,刪除成功表明token校驗經過,若是用select+delete來校驗token,
存在併發問題,不建議使用
複製代碼
悲觀鎖
獲取數據的時候加鎖獲取

select * from table_xxx where id=’xxx’ for update;

注意:id字段必定是主鍵或者惟一索引,否則是鎖表,會出事的。

悲觀鎖使用時通常伴隨事務一塊兒使用,數據鎖定時間可能會很長,根據實際狀況選用
複製代碼
樂觀鎖
樂觀鎖只是在更新數據那一刻鎖表,其餘時間不鎖表,因此相對於悲觀鎖,效率更高。

樂觀鎖的實現方式多種多樣能夠經過version或者其餘狀態條件:

1.經過版本號實現

update table_xxx set name=#name#,version=version+1 where version=#version#

2.經過條件限制

update table_xxx set avai_amount=avai_amount-#subAmount# where avai_amount-#subAmount# >= 0

要求:avai_amount-subAmount >=0
這個情景適合不用版本號,只更新是作數據安全校驗,適合庫存模型,扣份額和回滾份額,性能更高。

注意:樂觀鎖的更新操做,最好用主鍵或者惟一索引來更新,這樣是行鎖,不然更新時會鎖表,上面兩個sql改爲下面的兩個更好。

update table_xxx set name=#name#,version=version+1 where id=#id# and version=#version#

update table_xxx set avai_amount=avai_amount-#subAmount# where id=#id# and
avai_amount-#subAmount# >= 0
複製代碼
分佈式鎖
仍是拿插入數據的例子,若是是分佈是系統,構建全局惟一索引比較困難,例如惟一性的字段無法肯定,這時候能夠引入分佈式鎖,經過第三方的系統(redis或zookeeper),在業務系統插入數據或者更新數據,獲取分佈式鎖,而後作操做,以後釋放鎖,其實就是爲了控制多線程併發的操做,也是分佈式系統中常常用到的解決思路。

以上關於鎖的內容你們能夠閱讀下這篇文章加深瞭解分佈式鎖總結

select + insert併發不高的後臺系統,或者一些任務JOB,爲了支持冪等,支持重複執行,簡單的處理方法是,先查詢下一些關鍵數據,判斷是否已經執行過,在進行業務處理,就能夠了。注意:核心高併發流程不要用這種方法。狀態機冪等在設計單據相關的業務,或者是任務相關的業務,確定會涉及到狀態機(狀態變動圖),就是業務單據上面有個狀態,狀態在不一樣的狀況下會發生變動,通常狀況下存在有限狀態機,這時候,若是狀態機已經處於下一個狀態,這時候來了一個上一個狀態的變動,理論上是不可以變動的,這樣的話,保證了有限狀態機的冪等。注意:訂單等單據類業務,存在很長的狀態流轉,必定要深入理解狀態機,對業務系統設計能力提升有很大幫助。對外提供接口的api如何保證冪等如銀聯提供的付款接口:須要接入商戶提交付款請求時附帶:source來源,seq序列號source+seq在數據庫裏面作惟一索引,防止屢次付款,(併發時,只能處理一個請求)。重點:對外提供接口爲了支持冪等調用,接口有兩個字段必須傳,一個是來源source,一個是來源方序列號seq,這個兩個字段在提供方系統裏面作聯合惟一索引,這樣當第三方調用時,先在本方系統裏面查詢一下,是否已經處理過,返回相應處理結果;沒有處理過,進行相應處理,返回結果。注意,爲了冪等友好,必定要先查詢一下,是否處理過該筆業務,不查詢直接插入業務系統,會報錯,但實際已經處理了。最後總結:冪等性應該是合格程序員的一個基因,在設計系統時,是首要考慮的問題,尤爲是在像第三方支付平臺,銀行,互聯網金融公司等涉及的網上資金系統,既要高效,數據也要準確,因此不能出現多扣款,多打款等問題,這樣會很難處理,並會大大下降用戶體驗。

相關文章
相關標籤/搜索