假設有如下的業務流程,用戶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步:在事務回滾日志表將這次事務標識為成功;
結束步驟
結束哈;