在實(shí)際的項(xiàng)目中我們有很多操作,如api請(qǐng)求,訂單付款声离,發(fā)送消息带膜,防重復(fù)提交等。不管我們做了多少次扇售,其實(shí)應(yīng)該產(chǎn)生一樣的效果或返回一樣的結(jié)果前塔。這些就需要使用冪等的特性來(lái)支持。
什么是冪等承冰??jī)绲任易约豪斫鉃橄到y(tǒng)統(tǒng)一規(guī)則設(shè)計(jì)华弓,承諾只要調(diào)接口成功,多次調(diào)用的結(jié)果保持一致困乒。聲明為冪等的服務(wù)會(huì)認(rèn)為外部調(diào)用失敗是常態(tài)寂屏,并且失敗之后必然會(huì)有重試。
如何保證冪等:
1顶燕、token機(jī)制
業(yè)務(wù)場(chǎng)景:頁(yè)面數(shù)據(jù)只能被點(diǎn)擊一次凑保。
發(fā)生原因:由于重復(fù)點(diǎn)擊或者網(wǎng)絡(luò)重發(fā),或者nginx重發(fā)等情況會(huì)導(dǎo)致數(shù)據(jù)被重復(fù)提交
解決方式:采用token加redis(redis單線程的涌攻,處理需要排隊(duì))
基本的處理就是提交后臺(tái)校驗(yàn)token欧引,同時(shí)刪除token,生成新的token返回
關(guān)鍵點(diǎn):先刪除token,還是后刪除token
后刪除token:如果進(jìn)行業(yè)務(wù)處理成功后恳谎,刪除redis中的token失敗了芝此,這樣就導(dǎo)致了有可能會(huì)發(fā)生重復(fù)請(qǐng)求憋肖,因?yàn)閠oken沒(méi)有被刪除。這個(gè)問(wèn)題其實(shí)是數(shù)據(jù)庫(kù)和緩存redis數(shù)據(jù)不一致問(wèn)題婚苹,后續(xù)會(huì)寫(xiě)文章進(jìn)行講解岸更。
先刪除token:如果系統(tǒng)出現(xiàn)問(wèn)題導(dǎo)致業(yè)務(wù)處理出現(xiàn)異常,業(yè)務(wù)處理沒(méi)有成功膊升,接口調(diào)用方也沒(méi)有獲取到明確的結(jié)果怎炊,然后進(jìn)行重試,但token已經(jīng)刪除掉了廓译,服務(wù)端判斷token不存在评肆,認(rèn)為是重復(fù)請(qǐng)求,就直接返回了非区,無(wú)法進(jìn)行業(yè)務(wù)處理了瓜挽。
先刪除token可以保證不會(huì)因?yàn)橹貜?fù)請(qǐng)求,業(yè)務(wù)數(shù)據(jù)出現(xiàn)問(wèn)題征绸。出現(xiàn)業(yè)務(wù)異常久橙,可以讓調(diào)用方配合處理一下,重新獲取新的token管怠,再次由業(yè)務(wù)調(diào)用方發(fā)起重試請(qǐng)求就ok了淆衷。
token優(yōu)缺點(diǎn):
不足之處:就是要申請(qǐng),一次有效性排惨,額外的請(qǐng)求(一次獲取token請(qǐng)求吭敢、判斷token是否存在的業(yè)務(wù))。其實(shí)真實(shí)的生產(chǎn)環(huán)境中暮芭,1萬(wàn)請(qǐng)求也許只會(huì)存在10個(gè)左右的請(qǐng)求會(huì)發(fā)生重試鹿驼,為了這10個(gè)請(qǐng)求,我們讓9990個(gè)請(qǐng)求都發(fā)生了額外的請(qǐng)求
優(yōu)點(diǎn):可以限流
2辕宏、 悲觀鎖
獲取數(shù)據(jù)的時(shí)候加鎖獲取
select * from table_xxx where id='xxx' for update;
注意:id字段一定是主鍵或者唯一索引畜晰,不然是鎖表,會(huì)死人的
悲觀鎖使用時(shí)一般伴隨事務(wù)一起使用瑞筐,數(shù)據(jù)鎖定時(shí)間可能會(huì)很長(zhǎng)凄鼻,根據(jù)實(shí)際情況選用
3、樂(lè)觀鎖
樂(lè)觀鎖適合在更新的場(chǎng)景中聚假,update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 1
根據(jù)version版本块蚌,也就是在操作庫(kù)存前先獲取當(dāng)前商品的version版本號(hào),然后操作的時(shí)候帶上此version號(hào)膘格。流程為第一次操作庫(kù)存時(shí)峭范,得到version為1,調(diào)用庫(kù)存服務(wù)version變成了2瘪贱;但返回給訂單服務(wù)出現(xiàn)了問(wèn)題纱控,訂單服務(wù)又一次發(fā)起調(diào)用庫(kù)存服務(wù)辆毡,當(dāng)訂單服務(wù)傳如的version還是1,再執(zhí)行上面的sql語(yǔ)句時(shí)甜害,就不會(huì)執(zhí)行舶掖;因?yàn)関ersion已經(jīng)變?yōu)?了,where條件就不成立尔店。這樣就保證了不管調(diào)用幾次眨攘,只會(huì)真正的處理一次。樂(lè)觀鎖主要使用于處理讀多寫(xiě)少的問(wèn)題
4嚣州、防重表
適用于在業(yè)務(wù)中有唯一標(biāo)的插入場(chǎng)景中期犬,比如在以上的支付場(chǎng)景中,如果一個(gè)訂單只會(huì)支付一次避诽,所以訂單ID或者訂單編號(hào),支付標(biāo)號(hào)等可以作為唯一標(biāo)識(shí)璃谨。這時(shí)沙庐,我們就可以建一張去重表,并且把唯一標(biāo)識(shí)作為唯一索引佳吞,在我們實(shí)現(xiàn)時(shí)拱雏,把創(chuàng)建支付單據(jù)和寫(xiě)入去去重表,放在一個(gè)事務(wù)中底扳,如果重復(fù)創(chuàng)建铸抑,數(shù)據(jù)庫(kù)會(huì)拋出唯一約束異常,操作就會(huì)回滾衷模。
5鹊汛、唯一主鍵
這是利用了數(shù)據(jù)庫(kù)的主鍵唯一約束的特性,解決了在insert場(chǎng)景時(shí)冪等問(wèn)題阱冶。但主鍵的要求不是自增的主鍵刁憋,這樣就需要業(yè)務(wù)生成全局唯一的主鍵。
如果是分庫(kù)分表場(chǎng)景下木蹬,路由規(guī)則要保證相同請(qǐng)求下至耻,落地在同一個(gè)數(shù)據(jù)庫(kù)和同一表中,要不然數(shù)據(jù)庫(kù)主鍵約束就不起效果了镊叁,因?yàn)槭遣煌臄?shù)據(jù)庫(kù)和表主鍵不相關(guān)尘颓。