資料庫新增冪等操作 使用資料庫唯一鍵實現事務冪等性

2021-10-13 13:27:48 字數 3427 閱讀 3019

冪等性

概念在分布式系統中,冪等性是一致性方面的乙個重要概念。

冪等(idempotent、idempotence)是乙個數學與計算機學概念,常見於抽象代數中。

在程式設計中乙個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。

所謂「影響相同」,不是要求返回值完全相同,而且是指後續多餘的呼叫對系統的資料一致性不造成破壞。對於寫入類操作,如果第一次寫入是成功的,後續的寫入應該丟擲異常或者空操作,或者執行了寫入但是未對資料造成變化。對於讀取類操作,需要保證其實現上是真正的讀取,不能在讀操作中夾帶寫操作。

場景需要實現冪等性的典型場景有以下兩種:

客戶端發起的請求可能需要重試,請求的後端處理需要保證冪等

後端系統使用同步rpc呼叫或非同步訊息實現分布式事務,訊息的消費者需要保證冪等

必要性重試是降低系統失敗率的重要手段。

廣義上的rpc,包括客戶端對服務端的api呼叫、後端系統的內網呼叫、跨機房呼叫等。一次rpc大體上包括三個步驟:傳送請求、執行過程、接收響應。由於網路傳輸存在不確定性,導致rpc呼叫存在乙個陷阱,即有可能出現第

一、第二步都成功、第三步失敗的情況,此時rpc的呼叫方由於接收不到結果,無法判斷被呼叫方是否已經完成過程呼叫,只能按失敗處理。

通常rpc呼叫方會針對網路失敗進行重試。在上述情況下,如果遠端**不具備冪等性,卻進行了重試,將導致系統的資料一致性遭到破壞,本該只執行一次的事務被執行了兩次。

對於非同步訊息的消費者來講,也有同樣的問題。在手動ack的情況下,訊息的處理需要接收訊息、處理訊息、ack三步,ack的失敗也會導致相同的問題。

在交易類的系統(比如電商、**等)中,對非冪等的遠端過程進行重試,可能會導致超買超賣,對客戶造成經濟損失。

網際網路應用一般都是提供7*24服務的,而網際網路應用本身又是快速迭代,後端系統是隨時有可能需要進行發布的。發布等同於一次宕機(程序被kill),這意味著對於網際網路應用的後端系統,宕機是常態而非特例。這也是冪等性和重試的必要性**之一。

可重入性

冪等性在技術上同時要求可重入性。在分布式系統中,重試和併發都是常態,允許多次呼叫**也應該支援併發呼叫。分布式鎖(包括資料庫鎖)可以解決這一問題。

技術實現

本地事務的冪等性

考慮簡單的增刪改查四類操作:

select,天生冪等

insert,資料庫自增主鍵時不具備冪等

基於主鍵update,具備冪等,但是帶查詢的更新除外(形如update t set x = x + 1 where ...)

基於主建delete,具備冪等

非基於主鍵的udpate/delete操作,需要具體問題具體分析

在分布式系統中,帶條件的或帶子查詢的增刪改操作應當慎用,除了非冪等性問題以外,還有可能帶來死鎖問題。

很顯然,如果乙個事務中所有資料庫操作都是冪等操作,且不存在外部呼叫,那麼該事務一定也是冪等的。

如果事務中存在非冪等的資料庫操作,唯一鍵為本地事務的冪等性提供了實現基礎。使用帶有唯一鍵的去重表,在事務中先執行insert去重表,再執行其他業務操作,然後提交事務,事務過程**現異常回滾事務,可以保證事務的冪等性。

考慮兩種失敗的情況。

insert去重表失敗,事務回滾,無任何影響,呼叫方應停止重試

insert去重表成功,業務操作失敗,事務回滾,之前插入的記錄也將消失,無任何影響,呼叫方可以選擇重試

以上兩種失敗的情況下,事務的冪等性是可以保證的。

補充說明,在使用jdbc連線mysql的情況下,如果程式需要捕獲唯一鍵衝突異常,可以catch com.mysql.jdbc.exceptions.jdbc4.mysqlintegrityconstraintviolationexception。

本地事務併發情況

在併發的情況下,如果兩個(資料庫的)客戶端同時執行該事務且唯一鍵相同,此時唯一鍵能直到分布式鎖的作用。

這裡以示例來說明。開啟兩個命令列終端,連線同乙個mysql資料庫,稱之為a和b。

先在任意一端建立測試表

1create table tt (id int primary key);

在兩端依次執行

1set autocommit = false;

在a端執行

1insert into tt (id) values (1);

返回1query ok, 1 row affected (0.00 sec)

在b端執行同樣的insert語句

1insert into tt (id) values (1);

此時b端會掛起。

如果在a端執行

1commit;

a端會提交成功,而b端會立即結束掛起,報錯,等於獲取鎖失敗。

1error 1062 (23000): duplicate entry '1' for key 'primary'

如果a端執行

1rollback;

或直接關閉終端,b端則會立即結束掛起且insert成功,等於獲取鎖成功。

通過這個實驗,同時也可以得出,資料庫的唯一鍵可以用於實現通用的分布式鎖。

分布式事務的冪等性

要保證分布式事務的冪等性,需要各個子事務都保證冪等性,否則整體冪等性的很難實現。

在基於關係型資料庫的系統中,分布式事務最終還是要依靠資料庫的本地事務來實現事務的acid。

考慮存在a、b兩個子系統分別實現各自的事務,需要集成為乙個分布式事務,其中a系統的本地事務基於mysql實現,保證冪等;b系統的本地事務也保證冪等,對外提供rpc服務介面,其具體實現這裡不關心。

在這種情況下,有乙個簡單的不需要事務仲裁者的分布式事務方案,就是在a事務中巢狀對b的呼叫。具體流程是a事務先執行本地資料庫操作,再呼叫b介面,然後提交事務。

同樣,考慮三種失敗的情況。

a本地操作失敗,事務回滾,無任何影響

a本地操作成功,呼叫b失敗,如果是唯一性衝突導致的失敗,a正常提交;如果是其他失敗,事務回滾,此時外部呼叫方可以重試

很極端的情況,a本地操作成功,呼叫b成功,事務提交失敗,此時外部呼叫方可以重試

這個方案並不是乙個高可靠的解決方案,但是實現上非常簡單。

其缺陷在於如果外部呼叫方沒有進行重試,那麼可能會產生對b的多餘呼叫。當然,如果在業務上對b的少量多餘呼叫是可接受的,比如b是次要業務,那這個方案也是可以用的。

另外由於在db事務中巢狀了耗時操作(rpc呼叫),事務的耗時也因此延長,資料庫連線被事務占用不能復用,系統的吞吐量會受到影響。

分布式事務不可能實現絕對的一致性。我們應該從概率的角度思考問題,如果能將不一致的概率降到最低,比如保證冪等性加重試,再輔以業務監控和人工干預,就可以實現系統整體上較高的一致性。

冪等性設計

冪等性設計不能脫離業務來討論。一般情況下,去重表同時也是業務資料表。對於唯一鍵,這裡提供兩種設計思路:

在請求引數中附帶唯一標識作為唯一鍵,唯一標識可以由業務欄位加時間戳低位拼接而成,或者使用snowflake、uuid等id生成演算法

找出業務本身的唯一約束。比如乙個客戶對同一只新股只能申購一次,那麼客戶號加申購**可以組成唯一鍵

資料庫新增冪等操作 基於資料庫實現冪等介面

tl dr 通過唯一編號確定同一請求,沒有唯一編號的自行生成。資料庫記錄操作狀態,資料庫事務保證資料一致性。概述通過http api進行通訊的系統,在支付或者只允許操作一次的相關場景中,對介面的冪等性有嚴格要求。介面的冪等性體現在 請求執行成功所得到的結果與次數無關 如果介面沒有實現冪等性,對於轉賬...

Mysql實現冪等 基於資料庫實現冪等介面

tl dr 通過唯一編號確定同一請求,沒有唯一編號的自行生成。資料庫記錄操作狀態,資料庫事務保證資料一致性。概述通過http api進行通訊的系統,在支付或者只允許操作一次的相關場景中,對介面的冪等性有嚴格要求。介面的冪等性體現在 請求執行成功所得到的結果與次數無關 如果介面沒有實現冪等性,對於轉賬...

資料庫 資料庫基本操作

操作練習 修改表結構 表資料的操作 實現 1 建立表 create table student stu no char 12 not null primary key,stu name varchar 20 not null gender tinyint 1 default1,age tinyint...