接口冪等性

接口冪等性


冪等.png

一徐鹤、什么是冪等

冪等(Idempotence)是一個(gè)數(shù)學(xué)和計(jì)算科學(xué)概念丈屹,簡單的來說:一個(gè)操作多次執(zhí)行產(chǎn)生的結(jié)果與一次執(zhí)行產(chǎn)生的結(jié)果一致藕漱,不會(huì)因多次執(zhí)行而產(chǎn)生負(fù)面影響锅劝。如等電梯時(shí),我們按一次按鈕和按多次結(jié)果是一樣疫粥,或者刷公交卡時(shí)茬斧,二次刷卡會(huì)提示刷卡重復(fù)。

二手形、冪等發(fā)生場景

  1. 表單重復(fù)提交:

    在填寫頁面form表單時(shí)啥供,不小心快速保存了兩次,對應(yīng)的后臺產(chǎn)生了兩條相同的記錄库糠。

  2. 接口超時(shí)重試:

    為解決接口調(diào)用超時(shí)伙狐,我們引入了重試機(jī)制,但如果因?yàn)榫W(wǎng)絡(luò)抖動(dòng)瞬欧、臨時(shí)故障等問題導(dǎo)致第一次請求沒及時(shí)收到返回結(jié)果(實(shí)際上服務(wù)端業(yè)務(wù)處理成功)贷屎,那么就會(huì)重試接口,從而產(chǎn)生重復(fù)數(shù)據(jù)艘虎。

  3. 消息重復(fù)消費(fèi):

    mq消費(fèi)者在讀取消息時(shí)唉侄,如果讀取到重復(fù)的消息,也會(huì)導(dǎo)致重復(fù)消費(fèi)問題野建。

三属划、冪等發(fā)生在哪些操作

所有的操作最終對應(yīng)的數(shù)據(jù)庫都是CURD。因此冪等最終反映在select候生、update同眯、insert、delete上唯鸭。

  • 對于select查詢须蜗,無論查詢一次還是多次,其返回的結(jié)果都是一致的目溉,因此明肮,所以select查詢是天然冪等;

  • 對于delete刪除缭付,如果在不考慮返回結(jié)果情況下柿估,刪除一次或多次,其也是具有冪等性陷猫;

  • 對于insert新增官份,新增一次或多次只厘,對應(yīng)產(chǎn)生重復(fù)記錄烙丛,其不具有冪等性舅巷;

  • 對于update更新,如果是直接更新設(shè)置河咽,不管是執(zhí)行多少次钠右,都是冪等的(比如:update user set status=1 where id=1),但如果是更新計(jì)算忘蟹,那么就存在冪等問題(比如:update user set status=status+1 where id=1)飒房。

因此,冪等問題主要發(fā)生在新增操作以及更新計(jì)算操作中媚值。

四狠毯、如何保證冪等

  1. insert前先select判斷

    在執(zhí)行新增操作時(shí),先查詢是否存在褥芒,如果存在嚼松,執(zhí)行更新操作,如果不存在锰扶,才進(jìn)行新增操作献酗。方案簡單實(shí)用,但如果對于多節(jié)點(diǎn)并發(fā)場景中坷牛,該方案仍然會(huì)存在重復(fù)數(shù)據(jù)的冪等問題罕偎,需要結(jié)合其他方案進(jìn)行優(yōu)化。

1.png
  1. 增加唯一索引

    在表中建立唯一索引京闰,需要注意的是颜及,如果二次請求操作,需要對數(shù)據(jù)庫拋出DuplicateKeyException進(jìn)行捕獲蹂楣,正常返回處理成功結(jié)果俏站。

    2.png
  2. 引入token

    需要兩次請求完成一次業(yè)務(wù)操作,第一次請求獲取token捐迫,第二次請求帶上這個(gè)token乾翔,完成業(yè)務(wù)操作。

3.png
  1. 采用悲觀鎖
    悲觀鎖適合高并發(fā)并且對數(shù)據(jù)的準(zhǔn)確性要求很高的場景施戴,如支付反浓、庫存場景。悲觀鎖常用的是數(shù)據(jù)庫表的for update語句赞哗,原則:一鎖二判三更新
    如下對于支付場景案例雷则,多個(gè)端扣除賬戶金額時(shí),需要保證賬戶金額充足才能扣減肪笋,否則會(huì)造成預(yù)支情形月劈。引入悲觀鎖機(jī)制度迂,在每筆金額扣減時(shí),先查詢金額猜揪,對當(dāng)前用戶金額記錄進(jìn)行加鎖惭墓,防止并發(fā)修改數(shù)據(jù),導(dǎo)致并發(fā)事務(wù)預(yù)支扣減而姐。

    `begin;`
    
    `select * from user_account where userId=xxx for update;`
    
    `update user_account set account=account-pay where userId=xxx;`
    
    `commit;`
    
4.png
  1. 采用樂觀鎖
    由于悲觀鎖腊凶,鎖的記錄行,如商品庫存系統(tǒng)拴念,某類商品并發(fā)場景下會(huì)造成大量的請求累積钧萍,從而直接影響接口性能。為提高性能問題政鼠,采用樂觀鎖機(jī)制风瘦。樂觀鎖,通過在表中引入timestamp或version字段進(jìn)行版本控制更新公般。

    假設(shè)有一張商品庫存表goods_stock万搔,包含id,goodsId(商品id), stock(庫存數(shù)量),version(版本號)四個(gè)字段俐载。

    步驟1:根據(jù)商品id查詢庫存表信息
    
            `select goodsId, stock,version from goods_stock where goodId=xxx`
    
    步驟2:根據(jù)商品id和當(dāng)前版本號進(jìn)行扣減庫存
    
        `update goods_stock set stock=stock - buyCount,version=version+1 where goodsId=xxx and version=xxx`
    
    步驟3:如果更新失敗蟹略,進(jìn)入重試 。
    
5.png
  1. 采用分布式鎖
    不論是悲觀鎖遏佣,還是樂觀鎖挖炬,都是屬于數(shù)據(jù)庫層面上分布式鎖。還有基于zookeeper和redis的分布式鎖状婶,一般常用的redis來作為分布式鎖意敛,同時(shí)spring官網(wǎng)也推薦使用redisson這個(gè)框架api。這里不做詳細(xì)介紹膛虫,像分布式鎖單點(diǎn)問題草姻;集群后,加鎖成功稍刀,master未來及復(fù)制到slave上導(dǎo)致鎖信息丟失問題撩独;由于GC或網(wǎng)絡(luò)延遲導(dǎo)致的任務(wù)時(shí)間變長,從而導(dǎo)致執(zhí)行時(shí)間超過鎖過期時(shí)間账月,其他線程獲取鎖综膀;以及為解決以上問題,引入redLock是如何的原理局齿。在分布式鎖文章中再做詳細(xì)介紹剧劝。

  2. 建立防重表
    防重表,也是利用數(shù)據(jù)庫的唯一索引約束抓歼,在業(yè)務(wù)表所在的數(shù)據(jù)庫中單獨(dú)創(chuàng)建一個(gè)去重表讥此。每次請求來時(shí)拢锹,先執(zhí)行去重表插入記錄,如果成功執(zhí)行業(yè)務(wù)表邏輯萄喳,如果失敗卒稳,結(jié)束。
    這里取胎,有個(gè)小插曲展哭,很多人可能會(huì)疑惑,既然是用了唯一索引約束闻蛀,那又跟上面方案2唯一索引有啥區(qū)別的,為什么不直接在業(yè)務(wù)表里采用唯一索引進(jìn)行去重您市,反而單獨(dú)設(shè)計(jì)一張去重表里增加維護(hù)數(shù)據(jù)庫表的難度觉痛。是這樣的,我們的業(yè)務(wù)可能涉及到的不止一張表茵休,而唯一索引又是通過這個(gè)多個(gè)業(yè)務(wù)表的多個(gè)字段來確認(rèn)唯一性的薪棒,那么這時(shí)候,就只能通過去重表來去解決榕莺。

    6.png

  3. 采用狀態(tài)機(jī)
    對于有些業(yè)務(wù)存在業(yè)務(wù)狀態(tài)控制流轉(zhuǎn)俐芯,每個(gè)狀態(tài)都有前置狀態(tài)和后置狀態(tài),例如工單系統(tǒng):待審批钉鸯、審批中吧史、撤銷、審批通過唠雕、審批拒絕贸营。訂單支付系統(tǒng):待提交、待支付岩睁、已支付钞脂、取消。待提交的后置狀態(tài)為待支付捕儒,已支付的上一狀態(tài)必須為待支付狀態(tài)冰啃,取消的上一狀態(tài)必須為待支付狀態(tài)。
    狀態(tài)機(jī)方案從某一定的程度上可以理解為樂觀鎖的版本號方案刘莹,但不同的是前者基于業(yè)務(wù)層面的阎毅,后者基于數(shù)據(jù)庫層面,同時(shí)后者是遞增方式栋猖【谎Γ總體來說,處理的業(yè)務(wù)場景不同蒲拉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肃拜,一起剝皮案震驚了整個(gè)濱河市痴腌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌燃领,老刑警劉巖士聪,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異猛蔽,居然都是意外死亡剥悟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門曼库,熙熙樓的掌柜王于貴愁眉苦臉地迎上來区岗,“玉大人,你說我怎么就攤上這事毁枯〈鹊蓿” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵种玛,是天一觀的道長藐鹤。 經(jīng)常有香客問我,道長赂韵,這世上最難降的妖魔是什么娱节? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮祭示,結(jié)果婚禮上肄满,老公的妹妹穿的比我還像新娘。我一直安慰自己绍移,他們只是感情好悄窃,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蹂窖,像睡著了一般轧抗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞬测,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天横媚,我揣著相機(jī)與錄音,去河邊找鬼月趟。 笑死灯蝴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的孝宗。 我是一名探鬼主播穷躁,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼因妇!你這毒婦竟也來了问潭?” 一聲冷哼從身側(cè)響起猿诸,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狡忙,沒想到半個(gè)月后梳虽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灾茁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年窜觉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片北专。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡禀挫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逗余,到底是詐尸還是另有隱情特咆,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布录粱,位于F島的核電站,受9級特大地震影響画拾,放射性物質(zhì)發(fā)生泄漏啥繁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一青抛、第九天 我趴在偏房一處隱蔽的房頂上張望旗闽。 院中可真熱鬧,春花似錦蜜另、人聲如沸适室。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捣辆。三九已至,卻和暖如春此迅,著一層夾襖步出監(jiān)牢的瞬間汽畴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工耸序, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留忍些,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓坎怪,卻偏偏與公主長得像罢坝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子搅窿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 阿里巴巴 JAVA 開發(fā)手冊 1 / 32 Java 開發(fā)手冊 版本號 制定團(tuán)隊(duì) 更新日期 備 注 1.0.0 阿...
    糖寶_閱讀 7,518評論 0 5
  • 概念分布式一致性問題隨處可見嘁酿,任何一個(gè)實(shí)體/聯(lián)接模型隙券,都可能存在分布式一致性問題。如果把單機(jī)拆開來看痹仙,CPU是尔、內(nèi)存...
    田濤347閱讀 900評論 0 0
  • 1、安裝mysql (1)下載mysql的repo源:命令:wgethttp://repo.mysql.com/m...
    瘦到一百斤的藍(lán)胖子閱讀 249評論 0 0
  • 一开仰、編程規(guī)約 (一)命名規(guī)約 【強(qiáng)制】 代碼中的命名均不能以下劃線或美元符號開始拟枚,也不能以下劃線或美元符號結(jié)束。反...
    喝咖啡的螞蟻閱讀 1,490評論 0 2
  • 前言 本開發(fā)規(guī)范基于《阿里巴巴Java開發(fā)手冊終極版》修改众弓,并集成我們自己的項(xiàng)目開發(fā)規(guī)范恩溅,整合而成。 為表示對阿里...
    4ea0af17fd67閱讀 5,633評論 0 5