1. 程式人生 > >一個合格程式設計師的標準--------------冪等和非冪等

一個合格程式設計師的標準--------------冪等和非冪等

概念:

在web中的:(下面描述講解的是web)

            冪等:

                        對於同一種行為,如果執行不論多少次,最終的結果都是一致相同的,就稱這種行為是冪等的。

                        (個人理解:不管是一次,還是多次操作,我們返回同樣的結果,且不修改狀態資訊,介面可重複呼叫)

            非冪等:

                       對於同一種行為,如果最終的結果與執行的次數有關,每次執行後結果都不相同,就稱這種行為為非冪等。

    在數學上的概念:

                          這是個高等代數中的概念。簡而言之就是x^Y=x    x可能是任何元素,包括(數、矩陣等)

背景:

      1. 前端重複提交選中的資料,應該後臺只產生對應這個資料的一個反應結果;

      2. 我們發起一筆付款請求,應該只扣使用者賬戶一次錢,當遇到網路重發或系統bug重發,也應該只扣一次錢;

      3. 傳送訊息,也應該只發一次,同樣的簡訊發給使用者,使用者會哭的;

      4. 建立業務訂單,一次業務請求只能建立一個,建立多個就會出大問題等等很多重要的情況都需要冪等的特性來支援。 

技術的實現:

   1. 可以通過http協議(超文字傳輸協議):來區分。get,put,delete是天然的冪等操作,所以在儘量使用這些方法。

   2. 建立唯一索引,能防止新增的髒資料,比如在銀行的賬戶中,每個支付寶的資金賬戶,支付寶也有使用者賬戶,每個使用者只能有一個資金賬戶,怎麼防止給使用者建立銀行賬戶多個,那麼給賬戶表中的使用者ID加唯一索引,所以一個使用者新增成功一個資金賬戶記錄。要點:唯一索引或唯一組合索引來防止新增資料存在髒資料(當表存在唯一索引,併發時新增報錯時,再查詢一次就可以了,資料應該已經存在了,返回結果即可)。

   3. token + (redis) 的機制(token的特點 : 一次有效性,時效性,可以對流量進行控制),可以防止頁面的重複提交。具體實現 : 發生的原因由於重複的點選 , 網路原因導致了多次傳送請求,或者由於nginx重發等情況會導致資料被重複的提交 ; 解決的方案 : 

  •  在資料提交給後臺伺服器處理時,要向伺服器申請token(全域性唯一的變數),將token存放到redis(可以是其他的)中,設定token的有效時間。
  •  當伺服器接受到請求進行處理時,對token進行校驗,校驗通過後並刪除該token,並生成新的token返回

 4 . 悲觀鎖和樂觀鎖:(詳見)

  •  悲觀鎖 : 更新資料時認為此次操作會造成資料的缺失。 

注意 :  id欄位必須時主鍵或者唯一索引,不然的話就鎖了整個表,導致效率降低,必須開啟事務,缺點容易造成死鎖,不建議使用

  • 樂觀鎖 :  更新資料時認為此次操作不會造成資料缺失,所以只是在更新資料那一刻鎖表,其他時間不鎖表,相對於悲觀鎖的效率更高。

實現方式 : 在資料庫的表中增加一個欄位version(版本號)或者是其他的狀態位。在對錶進行操作時同時取出資料和標誌位version,當要修改資料表時,對version進行判斷是否與之前取出的一致,如果一致則修改,如果不一致則不修改。同時version自增一存入資料庫。 

(注意 :  注意:樂觀鎖的更新操作,最好用主鍵或者唯一索引來更新,這樣是行鎖,否則更新時會鎖表)

   5 . 分散式鎖,加入系統部署在分散式系統上,構建全域性的唯一索引比較困難(唯一的欄位無法沒法確定),這時候可以引入分散式鎖,通過第三方的系統(redis或zookeeper),在業務系統插入資料或者更新資料,獲取分散式鎖,然後做操作,之後釋放鎖,這樣其實是把多執行緒併發的鎖的思路,引入多多個系統,也就是分散式系統中得解決思路

要點:某個長流程處理過程要求不能併發執行,可以在流程執行之前根據某個標誌(使用者ID+字尾等)獲取分散式鎖,其他流程執行時獲取鎖就會失敗,也就是同一時間該流程只能有一個能執行成功,執行完成後,釋放分散式鎖(分散式鎖要第三方系統提供);

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

總結:

       冪等與你是不是分散式高併發還有JavaEE都沒有關係。關鍵是你的操作是不是冪等的。一個冪等的操作典型如:把編號為5的記錄的A欄位設定為0這種操作不管執行多少次都是冪等的。一個非冪等的操作典型如:把編號為5的記錄的A欄位增加1這種操作顯然就不是冪等的。要做到冪等性,從介面設計上來說不設計任何非冪等的操作即可。譬如說需求是:當用戶點選贊同時,將答案的贊同數量+1。改為:當用戶點選贊同時,確保答案贊同表中存在一條記錄,使用者、答案。贊同數量由答案贊同表統計出來。總之冪等性應該是合格程式設計師的一個基因,在設計系統時,是首要考慮的問題,尤其是在像支付寶,銀行,網際網路金融公司等涉及的都是錢的系統,既要高效,資料也要準確,所以不能出現多扣款,多打款等問題,這樣會很難處理,使用者體驗也不好。