深入理解幂等技术
什么是冪等
冪等(idempotent)是一個數學與計算機學概念,常見于抽象代數中。
在編程中,一個冪等操作的特點是,其任意多次執行所產生的影響均與一次執行的影響相同。冪等函數,或冪等方法,是指可以使用相同參數重復執行,并能獲得相同結果的函數。冪等函數可以改變系統的狀態。例如,setTrue()就是一個冪等函數,因為無論執行多少次,其結果都是一樣的。
冪等的場景有很多,例如:
- 前端重復提交選中的數據,后臺只產生對應這個數據的一個反應結果;
- 我們發起一筆付款請求,應該只扣用戶賬戶一次錢,當遇到網絡重發或系統bug重發,也應該只扣一次錢;
- 發送消息,也應該只發一次,同樣的短信發給用戶,用戶會崩潰;
- 創建業務訂單,一次業務請求只能創建一個,創建多個就會出大問題。
冪等的技術手段
冪等并不是并發場景下的特有問題。冪等處理的是多次執行的問題,而并發僅僅是多次執行的一種形式。不管是依次執行,還是并發執行,都需要做好冪等。有些技術人員將解決并發問題的技術手段,例如悲觀鎖、樂觀鎖和分布式鎖,當成冪等的技術手段,這是不對的。
再次強調,冪等的核心是確保唯一性。
唯一索引
在數據庫中建立唯一索引,用作冪等記錄,可以防止插入重復的數據。 在冪等函數中,先執行一次查詢操作,如存在冪等記錄則返回第一次執行的結果,如不存在冪等記錄則繼續執行。在并發場景下,可能存在多個線程同時插入冪等記錄,這時候唯一索引可以確保只有一個線程插入成功,其它線程拋出異常。
除了插入冪等記錄,應該還要插入其它的業務數據,這個時候務必使用事務。在實際工作中,冪等記錄與事務經常同時出現,如影相隨。
唯一數據
使用redis、memcache和zookeeper都可以實現唯一數據,這里僅用redis的SETNX舉例。筆者從redis的官方文檔摘抄了SETNX的用法,如下所示。
SETNX key value將 key 的值設為 value ,當且僅當 key 不存在。若給定的 key 已經存在,則 SETNX 不做任何動作。SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。可用版本: >= 1.0.0 時間復雜度: O(1) 返回值: 設置成功,返回 1 。 設置失敗,返回 0 。 復制代碼在冪等函數中,將唯一標識作為key,任取value,調用SETNX。如果返回1,說明當前是第一次執行,繼續執行冪等函數;如果返回0,取出第一次執行的結果并返回給調用方。在并發場景下,可能因尚未完成第一次執行而取不到結果,這時候可以稍作等待。
除了redis、memcache和zookeeper,還有其它手段可以實現唯一數據,讀者可自行探索。只要可以實現唯一數據,就可以用來做冪等。
狀態機約束
在單據相關的業務,或者是任務相關的業務,基本會涉及到狀態機。業務單據上面有個狀態,這個狀態根據一個有限狀態機進行跳轉。如果狀態機已經處于下一個狀態,這時候是不能往回跳轉到上一個狀態的。通過狀態機的跳轉約束,可以做到有限狀態機的冪等。
冪等的場景
冪等經常與事務同時出現,而事務適合小任務場景、不適合大任務場景,因此筆者將冪等場景分為以下兩類進行介紹。為了方便描述,我們假設bizId可以唯一標識一筆業務。
小任務場景
這個場景的處理方式很簡單,可以追求強一致性。在冪等函數中,先判斷冪等記錄是否存在。如果存在,直接返回;如果不存在,開啟一個事務。在事務中,采用任務名+bizId作為冪等組合字段,插入冪等記錄和業務數據。它的流程圖如下。
大任務場景
在大任務場景下,需要將大任務拆成多個小任務分別執行。在每個小任務中,都可以有事務。但是,沒有事務保證所有的小任務同時成功。因此,存在部分成功的場景。針對部分成功的場景,可以利用重試機制做到最終一致性。重試機制意味著多次執行,回到了冪等問題。這里只介紹需要冪等的場景。如果同時存在需要冪等和不需要冪等的場景,請加入一個判斷標。
同步執行小任務
同步執行小任務的流程圖如下。各小任務依次執行,中間的小任務不返回結果,僅在最后一個小任務或之后返回結果。
異步執行小任務
異步執行小任務的流程圖如下。各小任務單獨執行,互相不感知,也沒有地方返回結果。
冪等字段
不管是同步執行小任務,還是異步執行小任務,都需要為每個小任務設置一個冪等字段或冪等組合字段。筆者推薦采用小任務名+bizId作為冪等組合字段。一方面,bizId可以標識這一批小任務屬于同一筆業務;另一方便,小任務名可以區分不同的小任務。
碼字不易,如有建議請掃碼
總結
- 上一篇: Python 爬虫十六式 - 第六式:J
- 下一篇: 狼和鹿故事的作文