数据库新增幂等操作_使用数据库唯一键实现事务幂等性
冪等性
概念
在分布式系統(tǒng)中,冪等性是一致性方面的一個(gè)重要概念。
冪等(idempotent、idempotence)是一個(gè)數(shù)學(xué)與計(jì)算機(jī)學(xué)概念,常見于抽象代數(shù)中。
在編程中一個(gè)冪等操作的特點(diǎn)是其任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同。
所謂“影響相同”,不是要求返回值完全相同,而且是指后續(xù)多余的調(diào)用對系統(tǒng)的數(shù)據(jù)一致性不造成破壞。對于寫入類操作,如果第一次寫入是成功的,后續(xù)的寫入應(yīng)該拋出異常或者空操作,或者執(zhí)行了寫入但是未對數(shù)據(jù)造成變化。對于讀取類操作,需要保證其實(shí)現(xiàn)上是真正的讀取,不能在讀操作中夾帶寫操作。
場景
需要實(shí)現(xiàn)冪等性的典型場景有以下兩種:
客戶端發(fā)起的請求可能需要重試,請求的后端處理需要保證冪等
后端系統(tǒng)使用同步RPC調(diào)用或異步消息實(shí)現(xiàn)分布式事務(wù),消息的消費(fèi)者需要保證冪等
必要性
重試是降低系統(tǒng)失敗率的重要手段。
廣義上的RPC,包括客戶端對服務(wù)端的api調(diào)用、后端系統(tǒng)的內(nèi)網(wǎng)調(diào)用、跨機(jī)房調(diào)用等。一次RPC大體上包括三個(gè)步驟:發(fā)送請求、執(zhí)行過程、接收響應(yīng)。由于網(wǎng)絡(luò)傳輸存在不確定性,導(dǎo)致RPC調(diào)用存在一個(gè)陷阱,即有可能出現(xiàn)第一、第二步都成功、第三步失敗的情況,此時(shí)RPC的調(diào)用方由于接收不到結(jié)果,無法判斷被調(diào)用方是否已經(jīng)完成過程調(diào)用,只能按失敗處理。
通常RPC調(diào)用方會(huì)針對網(wǎng)絡(luò)失敗進(jìn)行重試。在上述情況下,如果遠(yuǎn)端代碼不具備冪等性,卻進(jìn)行了重試,將導(dǎo)致系統(tǒng)的數(shù)據(jù)一致性遭到破壞,本該只執(zhí)行一次的事務(wù)被執(zhí)行了兩次。
對于異步消息的消費(fèi)者來講,也有同樣的問題。在手動(dòng)ACK的情況下,消息的處理需要接收消息、處理消息、ACK三步,ACK的失敗也會(huì)導(dǎo)致相同的問題。
在交易類的系統(tǒng)(比如電商、證券等)中,對非冪等的遠(yuǎn)程過程進(jìn)行重試,可能會(huì)導(dǎo)致超買超賣,對客戶造成經(jīng)濟(jì)損失。
互聯(lián)網(wǎng)應(yīng)用一般都是提供7*24服務(wù)的,而互聯(lián)網(wǎng)應(yīng)用本身又是快速迭代,后端系統(tǒng)是隨時(shí)有可能需要進(jìn)行發(fā)布的。發(fā)布等同于一次宕機(jī)(進(jìn)程被kill),這意味著對于互聯(lián)網(wǎng)應(yīng)用的后端系統(tǒng),宕機(jī)是常態(tài)而非特例。這也是冪等性和重試的必要性來源之一。
可重入性
冪等性在技術(shù)上同時(shí)要求可重入性。在分布式系統(tǒng)中,重試和并發(fā)都是常態(tài),允許多次調(diào)用代碼也應(yīng)該支持并發(fā)調(diào)用。分布式鎖(包括數(shù)據(jù)庫鎖)可以解決這一問題。
技術(shù)實(shí)現(xiàn)
本地事務(wù)的冪等性
考慮簡單的增刪改查四類操作:
select,天生冪等
insert,數(shù)據(jù)庫自增主鍵時(shí)不具備冪等
基于主鍵update,具備冪等,但是帶查詢的更新除外(形如update t set x = x + 1 where ...)
基于主建delete,具備冪等
非基于主鍵的udpate/delete操作,需要具體問題具體分析
在分布式系統(tǒng)中,帶條件的或帶子查詢的增刪改操作應(yīng)當(dāng)慎用,除了非冪等性問題以外,還有可能帶來死鎖問題。
很顯然,如果一個(gè)事務(wù)中所有數(shù)據(jù)庫操作都是冪等操作,且不存在外部調(diào)用,那么該事務(wù)一定也是冪等的。
如果事務(wù)中存在非冪等的數(shù)據(jù)庫操作,唯一鍵為本地事務(wù)的冪等性提供了實(shí)現(xiàn)基礎(chǔ)。使用帶有唯一鍵的去重表,在事務(wù)中先執(zhí)行insert去重表,再執(zhí)行其他業(yè)務(wù)操作,然后提交事務(wù),事務(wù)過程中出現(xiàn)異常回滾事務(wù),可以保證事務(wù)的冪等性。
考慮兩種失敗的情況。
Insert去重表失敗,事務(wù)回滾,無任何影響,調(diào)用方應(yīng)停止重試
Insert去重表成功,業(yè)務(wù)操作失敗,事務(wù)回滾,之前插入的記錄也將消失,無任何影響,調(diào)用方可以選擇重試
以上兩種失敗的情況下,事務(wù)的冪等性是可以保證的。
補(bǔ)充說明,在使用JDBC連接MySQL的情況下,如果程序需要捕獲唯一鍵沖突異常,可以catch com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException。
本地事務(wù)并發(fā)情況
在并發(fā)的情況下,如果兩個(gè)(數(shù)據(jù)庫的)客戶端同時(shí)執(zhí)行該事務(wù)且唯一鍵相同,此時(shí)唯一鍵能直到分布式鎖的作用。
這里以示例來說明。打開兩個(gè)命令行終端,連接同一個(gè)MySQL數(shù)據(jù)庫,稱之為A和B。
先在任意一端創(chuàng)建測試表
1create table tt (id int primary key);
在兩端依次執(zhí)行
1set autocommit = false;
在A端執(zhí)行
1insert into tt (id) values (1);
返回
1Query OK, 1 row affected (0.00 sec)
在B端執(zhí)行同樣的insert語句
1insert into tt (id) values (1);
此時(shí)B端會(huì)掛起。
如果在A端執(zhí)行
1commit;
A端會(huì)提交成功,而B端會(huì)立即結(jié)束掛起,報(bào)錯(cuò),等于獲取鎖失敗。
1ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
如果A端執(zhí)行
1rollback;
或直接關(guān)閉終端,B端則會(huì)立即結(jié)束掛起且insert成功,等于獲取鎖成功。
通過這個(gè)實(shí)驗(yàn),同時(shí)也可以得出,數(shù)據(jù)庫的唯一鍵可以用于實(shí)現(xiàn)通用的分布式鎖。
分布式事務(wù)的冪等性
要保證分布式事務(wù)的冪等性,需要各個(gè)子事務(wù)都保證冪等性,否則整體冪等性的很難實(shí)現(xiàn)。
在基于關(guān)系型數(shù)據(jù)庫的系統(tǒng)中,分布式事務(wù)最終還是要依靠數(shù)據(jù)庫的本地事務(wù)來實(shí)現(xiàn)事務(wù)的ACID。
考慮存在A、B兩個(gè)子系統(tǒng)分別實(shí)現(xiàn)各自的事務(wù),需要整合為一個(gè)分布式事務(wù),其中A系統(tǒng)的本地事務(wù)基于MySQL實(shí)現(xiàn),保證冪等;B系統(tǒng)的本地事務(wù)也保證冪等,對外提供RPC服務(wù)接口,其具體實(shí)現(xiàn)這里不關(guān)心。
在這種情況下,有一個(gè)簡單的不需要事務(wù)仲裁者的分布式事務(wù)方案,就是在A事務(wù)中嵌套對B的調(diào)用。具體流程是A事務(wù)先執(zhí)行本地?cái)?shù)據(jù)庫操作,再調(diào)用B接口,然后提交事務(wù)。
同樣,考慮三種失敗的情況。
A本地操作失敗,事務(wù)回滾,無任何影響
A本地操作成功,調(diào)用B失敗,如果是唯一性沖突導(dǎo)致的失敗,A正常提交;如果是其他失敗,事務(wù)回滾,此時(shí)外部調(diào)用方可以重試
很極端的情況,A本地操作成功,調(diào)用B成功,事務(wù)提交失敗,此時(shí)外部調(diào)用方可以重試
這個(gè)方案并不是一個(gè)高可靠的解決方案,但是實(shí)現(xiàn)上非常簡單。
其缺陷在于如果外部調(diào)用方?jīng)]有進(jìn)行重試,那么可能會(huì)產(chǎn)生對B的多余調(diào)用。當(dāng)然,如果在業(yè)務(wù)上對B的少量多余調(diào)用是可接受的,比如B是次要業(yè)務(wù),那這個(gè)方案也是可以用的。
另外由于在DB事務(wù)中嵌套了耗時(shí)操作(RPC調(diào)用),事務(wù)的耗時(shí)也因此延長,數(shù)據(jù)庫連接被事務(wù)占用不能復(fù)用,系統(tǒng)的吞吐量會(huì)受到影響。
分布式事務(wù)不可能實(shí)現(xiàn)絕對的一致性。我們應(yīng)該從概率的角度思考問題,如果能將不一致的概率降到最低,比如保證冪等性加重試,再輔以業(yè)務(wù)監(jiān)控和人工干預(yù),就可以實(shí)現(xiàn)系統(tǒng)整體上較高的一致性。
冪等性設(shè)計(jì)
冪等性設(shè)計(jì)不能脫離業(yè)務(wù)來討論。一般情況下,去重表同時(shí)也是業(yè)務(wù)數(shù)據(jù)表。對于唯一鍵,這里提供兩種設(shè)計(jì)思路:
在請求參數(shù)中附帶唯一標(biāo)識(shí)作為唯一鍵,唯一標(biāo)識(shí)可以由業(yè)務(wù)字段加時(shí)間戳低位拼接而成,或者使用snowflake、UUID等ID生成算法
找出業(yè)務(wù)本身的唯一約束。比如一個(gè)客戶對同一只新股只能申購一次,那么客戶號(hào)加申購代碼可以組成唯一鍵
總結(jié)
以上是生活随笔為你收集整理的数据库新增幂等操作_使用数据库唯一键实现事务幂等性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IOC操作Bean管理XML方式(外部属
- 下一篇: error:java:无效的源发行版_I