高并發(fā)下如何保證接口的冪等性?
介紹
冪等性就是同一個(gè)操作執(zhí)行多次,產(chǎn)生的效果一樣。如http的get請(qǐng)求,數(shù)據(jù)庫(kù)的select請(qǐng)求就是冪等的
在分布式系統(tǒng)中,保證接口的冪等性非常重要,如提交訂單,扣款等接口都要保證冪等性,不然會(huì)造成重復(fù)創(chuàng)建訂單,重復(fù)扣款,那么如何保證接口的冪等性呢?
前端保證冪等性的方法
按鈕只能點(diǎn)擊一次
用戶點(diǎn)擊按鈕后將按鈕置灰,或者顯示loading狀態(tài)
RPG模式
即Post-Redirect-Get,當(dāng)客戶提交表單后,去執(zhí)行一個(gè)客戶端的重定向,轉(zhuǎn)到提交成功頁(yè)面。避免用戶按F5刷新導(dǎo)致的重復(fù)提交,也能消除按瀏覽器后退鍵導(dǎo)致的重復(fù)提交問題。目前絕大多數(shù)公司都是這樣做的,比如淘寶,京東等
后端保證冪等性的方法
使用唯一索引
對(duì)業(yè)務(wù)唯一的字段加上唯一索引,這樣當(dāng)數(shù)據(jù)重復(fù)時(shí),插入數(shù)據(jù)庫(kù)會(huì)拋異常
狀態(tài)機(jī)冪等
如果業(yè)務(wù)上需要修改訂單狀態(tài),例如訂單狀態(tài)有待支付,支付中,支付成功,支付失敗。設(shè)計(jì)時(shí)最好只支持狀態(tài)的單向改變。這樣在更新的時(shí)候就可以加上條件,多次調(diào)用也只會(huì)執(zhí)行一次。例如想把訂單狀態(tài)更新為支持成功,則之前的狀態(tài)必須為支付中
- update table_name set status = 支付成功 where status = 支付中
樂觀鎖實(shí)現(xiàn)冪等
- 查詢數(shù)據(jù)獲得版本號(hào)
- 通過版本號(hào)去更新,版本號(hào)匹配則更新,版本號(hào)不匹配則不更新
-- 假如查詢出的version為1
- -- 假如查詢出的version為1
- select version from table_name where userid = 10;
- -- 給用戶的賬戶加10
- update table_name set money = money -10, version = version + 1 where userid = 10 and version = 1
也可以通過條件來實(shí)現(xiàn)樂觀鎖,如庫(kù)存不能超賣,數(shù)量不能小于0
- update table_name set num = num - 10 where num - 10 >= 0
防重表
增加一個(gè)防重表,業(yè)務(wù)唯一的id作為唯一索引,如訂單號(hào),當(dāng)想針對(duì)訂單做一系列操作時(shí),可以向防重表中插入一條記錄,插入成功,執(zhí)行后續(xù)操作,插入失敗,則不執(zhí)行后續(xù)操作。本質(zhì)上可以看成是基于MySQL實(shí)現(xiàn)的分布式鎖。根據(jù)業(yè)務(wù)場(chǎng)景決定執(zhí)行成功后,是否刪除防重表中對(duì)應(yīng)的數(shù)據(jù)
分布式鎖實(shí)現(xiàn)冪等
執(zhí)行方法時(shí),先根據(jù)業(yè)務(wù)唯一的id獲取分布式鎖,獲取成功,則執(zhí)行,失敗則不執(zhí)行。分布式鎖可以基于redis,zookeeper,mysql來實(shí)現(xiàn),分布式鎖的細(xì)節(jié)就不介紹了
select+insert
先查詢一下有沒有符合要求的數(shù)據(jù),如果沒有再執(zhí)行插入。沒有并發(fā)的系統(tǒng)中可以保證冪等性,高并發(fā)下不要用這種方法,也會(huì)造成數(shù)據(jù)的重復(fù)插入。我一般做消息冪等的時(shí)候就是先select,有數(shù)據(jù)直接返回,沒有數(shù)據(jù)加分布式鎖進(jìn)行insert操作
全局唯一號(hào)實(shí)現(xiàn)冪等
通過source(來源)+ seq(序列號(hào))來判斷請(qǐng)求是否重復(fù),重復(fù)則直接返回請(qǐng)求重復(fù)提交,否則執(zhí)行。如當(dāng)多個(gè)三方系統(tǒng)調(diào)用服務(wù)的時(shí)候,就可以采用這種方式
本文轉(zhuǎn)載自微信公眾號(hào)「Java識(shí)堂」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java識(shí)堂公眾號(hào)。