簡單聊聊事務補償機制
假設有如下的業務流程,使用者1給使用者2轉賬100元:
轉賬服務需要執行如下操作:
第1步. 在資料庫連線1上執行:update 使用者表 set (使用者1的餘額) = (使用者1的餘額)- 100;
第2步. 在資料庫連線2上執行:update 使用者表 set (使用者2的餘額) = (使用者2的餘額)+ 100;
可能的問題:
1:第1步操作過程中,資料庫1掛了,轉賬服務無法得知對使用者1的扣款操作是否成功;
2:第1步操作成功,第2步操作失敗,轉賬服務回滾第1步的操作時,資料庫1掛了;
3:第1步操作成功,第2步操作過程中,資料庫2掛了,轉賬服務無法得知是否成功給使用者2加了錢;
基於上面的問題,產生了如下的資料庫設計:
轉賬流程變成了如下步驟:
第1步:
轉賬服務生成一個事務號,全域性唯一;
第2步:轉賬服務在資料庫1上執行事務:
開始事務:
update 使用者表 set (使用者1的餘額) = (使用者1的餘額)- 100;
insert 事務表 (事務號,成功)
結束事務:
第3步:轉賬服務在資料庫2上執行事務:
開始事務:
update 使用者表 set (使用者2的餘額) = (使用者2的餘額)+ 100;
insert 事務表 (事務號,成功)
結束事務:
這樣做的好處
當操作使用者1的賬戶失敗時,轉賬服務可以通過再次查詢資料庫1的事務表來判斷操作是否成功;
當操作使用者2的賬戶失敗時,轉賬服務可以通過再次查詢資料庫2的事務表來判斷操作是否成功;
接下來的問題:
當轉賬服務更新使用者1的賬戶成功後,接下來轉賬服務更新使用者2的賬戶之前,轉賬服務自己掛了;
這時,使用者1被扣了100,但是使用者2沒多出來100,資料不一致;
新的資料庫設計產生了,如下:
接下來的操作步驟變成了這樣:
轉賬服務的操作:
第1步:生成全域性唯一事務號;生成事務號對應的時間戳;
第2步:在回滾庫的日誌表中插入---“事務號開始”的操作;
第3步:在回滾庫的日誌表插入---“扣除使用者1的賬戶100元“的操作;
第4步:在資料庫1上執行事務:
開始事務:
update 使用者表 set (使用者1的餘額) = (使用者1的餘額)- 100;
insert 事務表 (事務號,成功)
結束事務:
第5步:在回滾庫的日誌表插入---“增加使用者2的賬戶100元”的操作;
第6步:在資料庫2上執行事務:
開始事務:
update 使用者表 set (使用者2的餘額) = (使用者2的餘額)+ 100;
insert 事務表 (事務號,成功)
結束事務:
第7步:在回滾庫的日誌表插入---“事務號結束”的操作;
回滾服務的操作:
假設轉賬超時時間是1小時;
定期檢查回滾庫中的回滾日誌表;
如果事務號對應結束,則忽略;
如果事務號沒有結束,但是事務沒超時,也忽略;
如果事務號沒有結束,事務超時,則按照回滾日誌,反向操作,對事務進行補償,補償步驟如下:
第1步:對使用者2進行事務補償,檢查資料庫2的使用者2的事務是否成功;
第2步:如果成功,則認為事務完成,在事務回滾日誌表中將這次事務標識為成功;並跳到“結束步驟”;
第3步:對使用者1進行事務補償,檢查資料庫1的使用者1的事務是否成功;
第4步:如果成功,則執行如下事務:
開始事務
update 使用者表 set (使用者1的餘額) = (使用者1的餘額)+ 100;
update 事務表 (事務號,回滾成功);
結束事務
第5步:在事務回滾日誌表將這次事務標識為成功;
結束步驟
結束哈;