2024-09-08 服務(wù)端開(kāi)發(fā)實(shí)戰(zhàn)

解決方案.gif

典型冪等策略

冪等處理的一般流程.png

唯一索引策略(極少使用)

1.select+insert+唯一索引沖突

//1.根據(jù)冪等號(hào)查詢(xún)冪等記錄
Record record = dao.select(param);
if (record != null){
//2.冪等記錄存在,直接返回冪等結(jié)果或根據(jù)記錄狀態(tài)(如失敗可重試)進(jìn)一步處理
}else{
    try{
        //3.冪等記錄不存在,插入冪等記錄
        dao.insert(entity);
        //4.插入成功,執(zhí)行業(yè)務(wù)邏輯
    }catch(DuplicateKeyException e){
        //5/插入失敗,若為重復(fù)異常,直接返回冪等結(jié)果或進(jìn)一步處理
        record = dao.select(param);
        //6.處理邏輯
    }catch(Throwable tr){
        //7.其他異常處理邏輯
    }
}

2.insert+唯一索引沖突

//1.插入冪等記錄
try{
    //2.插入成功,執(zhí)行業(yè)務(wù)邏輯
    dao.insert(entity);
}catch(DuplicateKeyException e){
    //3.插入失敗且為重復(fù)異常,直接返回冪等結(jié)果或進(jìn)一步處理
    dao.select(param);
    //4.處理邏輯
}catch(Throwable tr){
    //5.其他異常處理邏輯
}

數(shù)據(jù)庫(kù)開(kāi)銷(xiāo)大,如果重復(fù)請(qǐng)求發(fā)生的概率較小,可優(yōu)先選擇2.insert+唯一索引沖突。

唯一索引需結(jié)合冪等記錄狀態(tài)變更管控+事務(wù)機(jī)制(數(shù)據(jù)庫(kù)事務(wù)/分布式事務(wù))+鎖機(jī)制。

悲觀鎖策略(金融賬務(wù)清算域常用方式)

//1.開(kāi)啟事務(wù)
begin;
//2.基于冪等號(hào) biz_no 鎖行查詢(xún)
record = select * from table_name where biz_no='xxxx' for update
//3.沒(méi)有相關(guān)冪等記錄,說(shuō)明是首次請(qǐng)求
if (record == null){
    //4.初始化并插入冪等記錄
    insert(init(param))
    //5.再次基于冪等號(hào)biz_no鎖行查詢(xún)
    record = select * from table_name where biz_no='xxxx' for update
}

//6.鎖行查詢(xún)成功,根據(jù)記錄判斷決策處理方式
if (record.getStatus() != 預(yù)期狀態(tài)){
    //7.非預(yù)期狀態(tài),結(jié)束處理
    return;
}

//8.預(yù)期狀態(tài),執(zhí)行業(yè)務(wù)邏輯,如查詢(xún),更新流水等
//9.更新記錄
update table_name set status ='目標(biāo)狀態(tài)' where biz_no='xxx';
//10.提交事務(wù)
commit

通過(guò)串性化實(shí)現(xiàn)冪等,資源占用較多
依靠數(shù)據(jù)庫(kù)自身的特性來(lái)實(shí)現(xiàn),實(shí)現(xiàn)成本低,結(jié)合了事務(wù)機(jī)制保障了較強(qiáng)的數(shù)據(jù)一致性,解決了請(qǐng)求并發(fā)、亂序等的問(wèn)題

1.查詢(xún)冪等數(shù)據(jù)的狀態(tài),如果是不可重試終態(tài),直接返回;
2.如果是可重試終態(tài),則進(jìn)行重試(同一冪等號(hào)),涉及到下游則需要結(jié)合分布式事務(wù)
3.插入冪等記錄TransactionTemplate REQUIRED_NEW
4.下游處理成功,則變更冪等記錄狀態(tài)
5.下游也必須是冪等的。

分布式鎖

分布式冪等.png

SETNX keyName value,若keyName已存在于Redis中,則返回0;
若不存在,則返回1.

//1.嘗試將keyName寫(xiě)入緩存
if(redis.setNx(keyName, 1) == 1){
    //2.寫(xiě)入成功,即獲取鎖成功,繼續(xù)執(zhí)行業(yè)務(wù)邏輯
    //3.執(zhí)行完成,釋放鎖(為了防止誤釋放,可采用LUA腳本)
}else{
    //4.寫(xiě)入失敗,即獲取鎖失敗,要么直接返回,要么等待、重試
}

需結(jié)合事務(wù)機(jī)制和重試機(jī)制形成完整方案。
事務(wù)機(jī)制用于保證業(yè)務(wù)邏輯的數(shù)據(jù)一致性;重試機(jī)制是基于持久化的冪等記錄進(jìn)行失敗重試,保證最終一致性。
還存在釋放鎖操作可能失敗的情況。一旦釋放鎖操作失敗,就會(huì)導(dǎo)致一段時(shí)間內(nèi)記錄一直在緩沖中,其他線程無(wú)法獲得鎖。
即使設(shè)置了失效時(shí)間,在有效期內(nèi)仍會(huì)存在問(wèn)題。失效時(shí)間過(guò)短,業(yè)務(wù)邏輯執(zhí)行完前釋放鎖,失效時(shí)間過(guò)長(zhǎng),其他嘗試獲取鎖的過(guò)程就需要等待,甚至可能超時(shí)。

解決冪等問(wèn)題的關(guān)鍵

  • 1.唯一性約束
  • 2.執(zhí)行唯一性檢查

解決冪等問(wèn)題的實(shí)現(xiàn)方式
唯一索引:數(shù)據(jù)庫(kù)唯一索引,唯一索引可以基于業(yè)務(wù)流水建立,也可以單獨(dú)建表實(shí)現(xiàn);
唯一數(shù)據(jù):悲觀鎖、樂(lè)觀鎖、分布式鎖等鎖機(jī)制;
狀態(tài)機(jī)約束:對(duì)于存在狀態(tài)流轉(zhuǎn)的業(yè)務(wù),通過(guò)狀態(tài)機(jī)的流轉(zhuǎn)約束,可以實(shí)現(xiàn)有限狀態(tài)機(jī)的冪等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容