<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          冪等性如何保證

          共 3982字,需瀏覽 8分鐘

           ·

          2021-04-23 08:29

          ????關(guān)注后回復(fù) “進群” ,拉你進程序員交流群????

          作者丨sowhat1412

          來源丨sowhat1412

          1 冪等性

          1.1 定義

          冪等概念來自數(shù)學(xué),表示對數(shù)據(jù)源做N次變換和1次變換的結(jié)果是相同的。在工程中冪等性用來表示用戶對于同一操作發(fā)起的一次請求或者多次請求的結(jié)果是一致的,不會因為多次點擊而產(chǎn)生了副作用。

          1. 冪等包括第一次請求的時候?qū)Y源產(chǎn)生了副作用,但是以后的多次請求都不會再對資源產(chǎn)生副作用。

          2. 冪等關(guān)注的是以后的多次請求是否對資源產(chǎn)生的副作用,而不關(guān)注結(jié)果。

          3. 網(wǎng)絡(luò)超時等問題,不是冪等的討論范圍。

          4. 冪等性是系統(tǒng)服務(wù)對外一種承諾,而不是實現(xiàn),承諾只要調(diào)用接口成功,外部多次調(diào)用對系統(tǒng)的影響是一致的。聲明為冪等的服務(wù)會認為外部調(diào)用失敗是常態(tài),并且失敗之后必然會有重試。

          1.2 場景

          業(yè)務(wù)開發(fā)時,可能會遇到由于網(wǎng)絡(luò)震蕩導(dǎo)致請求無法收到導(dǎo)致觸發(fā)了重試機制,或者前端抖動導(dǎo)致表單重復(fù)提交這樣的情況。比如在交易系統(tǒng)中,用戶提交購物請求已經(jīng)被服務(wù)器端正確處理,但服務(wù)器端的返回結(jié)果由于網(wǎng)絡(luò)等原因被掉丟了,導(dǎo)致客戶端無法得知處理結(jié)果。如果是在網(wǎng)頁上,一些不恰當(dāng)?shù)脑O(shè)計可能會使用戶認為上一次操作失敗了,然后刷新頁面,這就導(dǎo)致了扣款被調(diào)用兩次,賬戶也被多扣了一次錢。此時就需要引入冪等性接口了。

          我們以MySQL為例,只有第三種場景需要開發(fā)人員使用其他策略保證冪等性:

          SELECT col1 FROM tab1 WHER col2=2
          -- 無論執(zhí)行多少次都不會改變狀態(tài),是天然的冪等。
          UPDATE tab1 SET col1=1 WHERE col2=2
          -- 無論執(zhí)行成功多少次狀態(tài)都是一致的,因此也是冪等操作。
          UPDATE tab1 SET col1=col1+1 WHERE col2=2
          -- 每次執(zhí)行的結(jié)果都會發(fā)生變化,這種不是冪等的。

          這里說下重復(fù)提交跟冪等性的區(qū)別:

          1. 重復(fù)提交是在第一次請求已經(jīng)成功的情況下,人為的進行多次操作,導(dǎo)致不滿足冪等要求的服務(wù)多次改變狀態(tài)。

          2. 冪等更多使用的情況是第一次請求不知道結(jié)果(比如超時)或者失敗的異常情況下,發(fā)起多次請求,目的是多次確認第一次請求成功,卻不會因多次請求而出現(xiàn)多次的狀態(tài)變化。

          1.3 冪等性思考

          引入冪等性后會使得服務(wù)端邏輯更加復(fù)雜,滿足冪等性的服務(wù)需要在邏輯中至少包含兩點:

          1. 首先去查詢上一次的執(zhí)行狀態(tài),如果沒有則認為是第一次請求。

          2. 在服務(wù)改變狀態(tài)的業(yè)務(wù)邏輯前,保證防重復(fù)提交的邏輯。

          冪等性可以簡化客戶端邏輯處理,但卻增加了服務(wù)提供者的邏輯和成本,所以是否要用,需根據(jù)具體場景具體分析,因此除了業(yè)務(wù)上的特殊要求外,盡量不提供冪等的接口。

          1. 增加了額外控制冪等的業(yè)務(wù)邏輯,復(fù)雜化了業(yè)務(wù)功能。

          2. 把并行執(zhí)行的功能改為串行執(zhí)行,降低了執(zhí)行效率。

          2 冪等性解決

          2.1 前端設(shè)置

          在用戶點擊完提交按鈕后,我們可以把按鈕設(shè)置為不可用或者隱藏狀態(tài)。


          前端限制比較簡單,但有個致命錯誤,如果碰到懂行的用戶通過模擬網(wǎng)頁請求來重復(fù)提交請求,繞過了前端限制。

          2.2 唯一索引

          防止訂單多次插入的最簡單直接方法就是創(chuàng)建唯一索引,然后插入的時候可能語句有細微的不同。但目的都是保證相同記錄在數(shù)據(jù)庫中只存在一條。

          1. 方法一:給數(shù)據(jù)庫添加唯一索引,然后如果執(zhí)行時捕捉到了DuplicateKeyException會明白是重復(fù)插入導(dǎo)致的,繼續(xù)往下執(zhí)行業(yè)務(wù)即可。

          2. 方法二:利用MySQL自帶的關(guān)鍵字ON DUPLICATE KEY UPDATE 實現(xiàn)不存在則插入,存在則更新的操作,該關(guān)鍵字不會刪除原有的記錄。

          3. 方法三:replace into 主要作用類似 INSERT 插入操作,replace into底層是先刪除后插入數(shù)據(jù),會破壞索引、重新維護索引。需注意必須要有主鍵或唯一索引才能有效,否則replace into就只新增了。

          2.3 去重表

          去重表的機制是根據(jù)mysql唯一索引的特性來的,大致流程:

          1. 客戶端先請求服務(wù)端,服務(wù)端先將這次的請求信息存入一張mysql的去重表中,這張表要根據(jù)這次請求的其中某個特殊字段建立唯一索引,或者主鍵索引。

          2. 判斷是否插入成功,如果插入成功,則繼續(xù)做后續(xù)業(yè)務(wù)請求。如果插入失敗,則代表已經(jīng)執(zhí)行過當(dāng)前請求。

          2.4 悲觀鎖


          方式一:簡單的利用Java自帶的syn 或 lock 鎖實現(xiàn)冪等性。核心點在于將重要的執(zhí)行部分將并行切換為串行。缺點是這個鎖在分布式場景是不能用的,因為都跨JVM了!此時需要引入分布式鎖了。

          依靠MySQL自帶的for update操作數(shù)據(jù)庫,來實現(xiàn)串行化。這里的重點在于for update,簡單說明下:

          1. 當(dāng)線程A執(zhí)行for update,數(shù)據(jù)會對當(dāng)前記錄加鎖,其他線程執(zhí)行到此行代碼的時候,會等待線程A釋放鎖之后,才可以獲取鎖,繼續(xù)后續(xù)操作。

          2. 事物提交時,for update獲取的鎖會自動釋放。

          該模式的缺點是,如果業(yè)務(wù)處理比較耗時,并發(fā)情況下,后面線程會長期處于等待狀態(tài),占用了很多線程,讓這些線程處于無效等待狀態(tài),而web服務(wù)中的線程數(shù)量一般有限的,如果大量線程由于獲取for update鎖處于等待狀態(tài),不利于系統(tǒng)并發(fā)操作。

          2.5 樂觀鎖

          對每行數(shù)據(jù)添加個version字段,這里其實跟秒殺設(shè)計中的思路類似,利用MySQL自帶的當(dāng)前讀更新操作。在更新數(shù)據(jù)時候先查詢獲得對應(yīng)版本號,然后嘗試update操作,根據(jù)返回值是否為0來確保是否是重復(fù)提交。

          select id,name,account,version from user where id = 1412; // 假設(shè)獲得的 version = 10

          update user set account = account + 10,version = version + 1 
          where id = 1412 and version = 10;

          2.6 分布式鎖

          使用Redis中的setnx操作,將冪等性的保證屏障設(shè)置在分布式鎖中。如果setnx成功了說明這是第一次進行數(shù)據(jù)插入,繼續(xù)執(zhí)行SQL語句即可。如果setnx失敗了,那說明已經(jīng)執(zhí)行過了。

          2.7 token 方案

          這種方式分成兩個階段:申請token階段和支付階段。

          1. 第一階段:在進入到提交訂單頁面之前,需要訂單系統(tǒng)根據(jù)用戶信息向支付系統(tǒng)發(fā)起一次申請token的請求,支付系統(tǒng)將token保存到Redis緩存中,為第二階段支付使用。

          2. 第二階段:訂單系統(tǒng)拿著申請到的token發(fā)起支付請求,支付系統(tǒng)會檢查Redis中是否存在該token,如果存在,表示第一次發(fā)起支付請求,刪除緩存中token后開始支付邏輯處理;如果緩存中不存在,表示非法請求。

          實際上這里的token可以認為是一個信物,支付系統(tǒng)根據(jù)token確認插入的唯一性。token模式不足之處在于,需要系統(tǒng)間交互兩次,流程較上述方法復(fù)雜。

          token

          參考

          1. MySQL操作:

            https://blog.csdn.net/qq_18975791/article/details/107285455

          2. 蘇三說冪等:

            https://mp.weixin.qq.com/s/v4CamZlqHP8gEmubzPme4g

          3. 冪等性實現(xiàn):

            https://blog.csdn.net/wanglei303707/article/details/88298211

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點這里好文分享給更多人↓↓

          瀏覽 30
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色婷在线视频 | 四虎亚洲 | 噜噜噜av | 91无码人妻精品1国产四虎 | 无码一区二区三区在线观看 |