淺談接口冪等性

前言

冪等性钞啸,是開發(fā)人員在日常開發(fā)中必須要考慮的梯澜,尤其是轉(zhuǎn)賬、支付等涉及金額交易的場景枕赵,如果出現(xiàn)冪等性的問題猜欺,造成的后果是非常嚴(yán)重的。

本文將分享一下什么是冪等性以及如何保證冪等性拷窜。

什么是冪等性

冪等(idempotent开皿、idempotence)是一個數(shù)學(xué)與計(jì)算機(jī)學(xué)概念,常見于抽象代數(shù)中篮昧。

在編程中一個冪等操作的特點(diǎn)是其任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同赋荆。冪等函數(shù),或冪等方法懊昨,是指可以使用相同參數(shù)重復(fù)執(zhí)行窄潭,并能獲得相同結(jié)果的函數(shù)。這些函數(shù)不會影響系統(tǒng)狀態(tài)酵颁,也不用擔(dān)心重復(fù)執(zhí)行會對系統(tǒng)造成改變嫉你。

冪等性產(chǎn)生原因

  • 前端未做限制,導(dǎo)致用戶重復(fù)提交

  • 使用瀏覽器后退材义,或者按F5刷新均抽,或者使用歷史記錄嫁赏,重復(fù)提交表單

  • 網(wǎng)絡(luò)波動其掂,引起重復(fù)請求

  • 超時重試,引起接口重復(fù)調(diào)用

  • 定時任務(wù)設(shè)置不合理潦蝇,導(dǎo)致數(shù)據(jù)重復(fù)處理

  • 使用消息隊(duì)列時款熬,消息重復(fù)消費(fèi)

如何保證冪等性

1.前端處理

  • 提交按鈕點(diǎn)擊置灰,或者增加loading

  • 頁面重定向(PRG)攘乒,PRG模式即POST-REDIRECT-GET贤牛,當(dāng)用戶進(jìn)行表單提交時,會重定向到另外一個提交成功頁面则酝,而不是停留在原先的表單頁面殉簸。這樣就避免了用戶刷新導(dǎo)致重復(fù)提交。同時防止了通過瀏覽器按鈕前進(jìn)/后退導(dǎo)致表單重復(fù)提交沽讹。

2.先select后insert + 唯一索引沖突

在保存數(shù)據(jù)前般卑,我們需要先select一下數(shù)據(jù)是否存在。如果數(shù)據(jù)已存在爽雄,則返回失旘鸺臁(具體操作視業(yè)務(wù)情況而定),如果數(shù)據(jù)不存在挚瘟,則執(zhí)行insert操作叹谁。

但在高并發(fā)的場景下饲梭,可能會出現(xiàn)兩個請求select的時候,都沒有查到數(shù)據(jù)焰檩,然后都執(zhí)行了insert操作憔涉,所以此時會有重復(fù)數(shù)據(jù)產(chǎn)生,因此在數(shù)據(jù)庫中析苫,我們需要添加唯一索引來保證冪等监氢。

流程圖如下:

先查后插+唯一索引.png

此方案適用于新增操作的接口,如用戶注冊藤违。

3.建去重表

某些業(yè)務(wù)場景浪腐,是允許重復(fù)數(shù)據(jù)存在的,僅在流程的某個環(huán)節(jié)才不允許出現(xiàn)重復(fù)數(shù)據(jù)顿乒,這種情況直接在表中添加唯一索引是不合適的议街,所以就需要創(chuàng)建一張去重表。

CREATE TABLE `table_name` (
  `id` bigint(15) NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
  `order_id` varchar(100) NOT NULL COMMENT '訂單號',
  `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `index_order_id` (`order_id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='去重表';

流程圖如下:

去重表.png

特別注意璧榄,防重表與業(yè)務(wù)表必須在同一數(shù)據(jù)庫特漩,并且操作要在同一事務(wù)中。

此方案適用于在業(yè)務(wù)中有唯一標(biāo)識的插入場景中骨杂,比如在支付業(yè)務(wù)中涂身,若一個訂單只會支付一次,則訂單ID可以作為唯一標(biāo)識搓蚪。

4.使用悲觀鎖

悲觀鎖蛤售,正如其名,具有強(qiáng)烈的獨(dú)占和排他特性妒潭。它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù)悴能,以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此雳灾,在整個數(shù)據(jù)處理過程中漠酿,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實(shí)現(xiàn)谎亩,往往依靠數(shù)據(jù)庫提供的鎖機(jī)制(也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性炒嘲,否則,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制匈庭,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))夫凸。

在交易場景中,用戶賬戶余額有100元嚎花,轉(zhuǎn)出50元寸痢,正常情況下用戶的余額剩余50元。

update account set amount-50 where id = 123;

如果此時有多個相同的請求紊选,可能會導(dǎo)致用戶的金額變?yōu)樨?fù)數(shù)啼止。所以此時可以使用悲觀鎖道逗,將用戶的行數(shù)據(jù)鎖住,在同一時刻只允許一個請求獲得鎖献烦,其他請求等待滓窍。

select * from account where id = 123 for update;

流程圖如下:

悲觀鎖.png

需要特別注意的是:如果使用的是mysql數(shù)據(jù)庫,存儲引擎必須用innodb巩那,因?yàn)樗胖С质聞?wù)吏夯。此外,這里id字段一定要是主鍵或者唯一索引即横,不然會鎖住整張表噪生。

因?yàn)楸^鎖是需要在同一事務(wù)中鎖住一行數(shù)據(jù),所以如果事務(wù)比較長东囚,會造成大量請求等待跺嗽,影響接口性能。

5.使用樂觀鎖

樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言页藻,樂觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制桨嫁。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實(shí)現(xiàn),以保證操作最大程度的獨(dú)占性份帐。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷璃吧,特別是對長事務(wù)而言,這樣的開銷往往無法承受废境。而樂觀鎖機(jī)制在一定程度上解決了這個問題畜挨。樂觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實(shí)現(xiàn)彬坏。何謂數(shù)據(jù)版本朦促?即為數(shù)據(jù)增加一個版本標(biāo)識,在基于數(shù)據(jù)庫表的版本解決方案中栓始,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實(shí)現(xiàn)。讀取出數(shù)據(jù)時血当,將此版本號一同讀出幻赚,之后更新時,對此版本號加一臊旭。此時落恼,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對,如果提交的數(shù)據(jù)版本號等于數(shù)據(jù)庫表當(dāng)前版本號离熏,則予以更新佳谦,否則認(rèn)為是過期數(shù)據(jù)。

樂觀鎖主要基于版本標(biāo)識(version)進(jìn)行操作滋戳,即每次操作查詢數(shù)據(jù)時都要先查詢出版本標(biāo)識(version)钻蔑,然后根據(jù)版本標(biāo)識(version)進(jìn)行update操作啥刻。

select id,amount,version from account id = 123;
update account set amount=amount-50,version=version+1 where id=123 and version = 1;

當(dāng)多個相同的請求查詢信息時,版本標(biāo)識是相同的咪笑,當(dāng)其中一個請求完成update操作可帽,后續(xù)請求影響條數(shù)均為0。

流程圖如下:

樂觀鎖.png

6.根據(jù)狀態(tài)機(jī)

很多時候窗怒,業(yè)務(wù)流程是有狀態(tài)流轉(zhuǎn)的映跟,這個時候可以使用狀態(tài)機(jī)來保證冪等性。

如訂單業(yè)務(wù)中扬虚,存在狀態(tài)「1-已下單努隙,2-已支付,3-已完成辜昵,4-已取消」剃法,按照業(yè)務(wù)流程,狀態(tài)是依次流轉(zhuǎn)的路鹰,所以在update操作時贷洲,我們就要根據(jù)本次的狀態(tài)來更新下一次的狀態(tài)。

update order_info set status = 3 where id = 123 and status = 2;

流程圖如下:

狀態(tài)機(jī).png

7.使用分布式鎖

分布式鎖的邏輯是晋柱,每次請求都通過業(yè)務(wù)唯一ID來嘗試獲取鎖优构,如果獲取成功,就進(jìn)行后續(xù)業(yè)務(wù)邏輯操作雁竞,如果獲取失敗钦椭,就舍棄請求直接返回。

分布式鎖通常是基于redis來實(shí)現(xiàn)的碑诉。

流程圖如下:

分布式鎖.png

分布式鎖是通過設(shè)置redis的過期時間來進(jìn)行控制彪腔。如果過期時間設(shè)置太短,則無法有效防止重復(fù)請求进栽;如果過期時間設(shè)置太長德挣,則影響redis存儲空間,甚至?xí)绊懞罄m(xù)業(yè)務(wù)操作快毛。因此需要根據(jù)具體的業(yè)務(wù)情況格嗅,來設(shè)置合理的過期時間。

8.基于token機(jī)制

此方案包含兩個請求階段:

1.客戶端請求服務(wù)端申請獲取token

2.客戶端攜帶token再次請求唠帝,服務(wù)端校驗(yàn)token后進(jìn)行操作屯掖。

流程圖如下:

token機(jī)制.png

這里有一個注意的點(diǎn):

服務(wù)端驗(yàn)證token是否存在,要使用刪除key的方式襟衰,即redis.del(key)贴铜,刪除成功則表示校驗(yàn)token通過;

不能使用先查再刪的操作,即先redis.get(key)绍坝,后redis.del(key)徘意,這種方式在高并發(fā)下無法保證冪等。


參考資料

如何實(shí)現(xiàn)接口冪等性

高并發(fā)下如何保證接口的冪等性陷嘴?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末映砖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子灾挨,更是在濱河造成了極大的恐慌邑退,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件劳澄,死亡現(xiàn)場離奇詭異地技,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)秒拔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門莫矗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人砂缩,你說我怎么就攤上這事作谚。” “怎么了庵芭?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵妹懒,是天一觀的道長。 經(jīng)常有香客問我双吆,道長眨唬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任好乐,我火速辦了婚禮匾竿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蔚万。我一直安慰自己岭妖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布笛坦。 她就那樣靜靜地躺著区转,像睡著了一般。 火紅的嫁衣襯著肌膚如雪版扩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天侄泽,我揣著相機(jī)與錄音礁芦,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛柿扣,可吹牛的內(nèi)容都是我干的肖方。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼未状,長吁一口氣:“原來是場噩夢啊……” “哼俯画!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起司草,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤艰垂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后埋虹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猜憎,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年搔课,在試婚紗的時候發(fā)現(xiàn)自己被綠了胰柑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡爬泥,死狀恐怖柬讨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情袍啡,我是刑警寧澤踩官,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站葬馋,受9級特大地震影響卖鲤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜畴嘶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一蛋逾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窗悯,春花似錦区匣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至欺旧,卻和暖如春姑丑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辞友。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工栅哀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留震肮,地道東北人。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓留拾,卻偏偏與公主長得像戳晌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子痴柔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350

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

  • 一. 冪等的概念 用在編程領(lǐng)域里, 則意為: 對同一個系統(tǒng)沦偎,使用同樣的條件,一次請求和重復(fù)的多次請求對系統(tǒng)資源的影...
    duenboa閱讀 6,283評論 0 3
  • 前言 通常,我們拿到一臺服務(wù)器后使用338端口遠(yuǎn)程桌面登錄windows系統(tǒng)咳蔚,使用22端口ssh登錄linux系統(tǒng)...
    Kayden_龍邵仁閱讀 784評論 0 0
  • 一豪嚎、什么是嵌入式測試 嵌入式軟件測試的概念似乎沒那么大眾,很多人從字面上理解屹篓,可能會以為這是個硬件測試疙渣,那么嵌入式...
    UTP協(xié)同自動化測試閱讀 958評論 0 0
  • 戰(zhàn)國時期東周這位大臣的辯才確實(shí)高明,但有些不太光彩 文\常清君 鄭重申明:常清君在自媒體平臺發(fā)布的每一篇文章堆巧,都是...
    此生讀寫伴閱讀 150評論 0 0
  • 1.前言(老司機(jī)直接跳過) 為什么js需要加密 談到加密妄荔,大多數(shù)人應(yīng)用場景都在于后端接口的加密簽名校驗(yàn)。這種一般...
    麻瓜三號閱讀 1,307評論 0 0