說下背景吧,公司最近打算用Seata來保證SpringCloud微服務間的全局事務(AT模式),使用的是Seata-server 1.1.0版本。注冊方式 Eureka,seata-server在測試環境部署了2台!
異常情況:
測試的時候 A服務為發起端 A->B->C 其中B服務在一次方法調用中 多次調用C服務更改同一條記錄(數據的創建以及更改狀態new->wait_pay->pay->success)。於是在A發起的請求結束時 B實際調用了3次C服務,導致C服務里有3條undo_log日志,之后 B服務發生了異常,C服務(每次都完成了本地事務)進行數據回滾(全局事務undo_log),此時正常情況應當是按照3條記錄 1 2 3創建的后先順序執行回滾,即 3 2 1 這就能保證數據正常回滾!
然而!!!在多次測試中發現數據回滾有時並不一定是按照 3 2 1 順序執行,時常 會從2 開始 這就導致 2中的記錄值 和當前數據庫值的實際值 有偏差(2中記錄修改后的狀態為pay,實際數據庫里的狀態是3中更改后記錄的狀態success)這就導致了回滾鏡像對比發現數據異常,認為是臟數據產生了!無法正確造成回滾!!!
找原因:
從結果上看 數據無法回滾 是由於 回滾時候 undo_log執行的順序異常導致的,以此切入!跟蹤seata-server的代碼發現,分支事務回滾時 會根據全局事務xid到branch_table中按照記錄生成的時間(`gmt_create`) 正序查詢所有分支事件記錄放入List中,之后從List里倒序取出,挨個執行回滾! 然后驚奇的發現 每次出現上述回滾異常 都是因為 有兩條或多條branch_table記錄的 gm_create是相同的 以致於后續回滾查詢分支事務的時候 無法保證其先后順序,而后執行回滾的順序 就一樣無法保證,才最終導致 上述錯誤!
解決方案:
問題找到了,既然按時間順序查找不靠譜 那找個靠譜的值來查詢不就行了,其中發現branch_id其實相對來說是遞增的 但是只相對於同一個服務而言,
private static final AtomicLong UUID = new AtomicLong(1000);
/**
* Generate uuid long.
*
* @return the long
*/
public static long generateUUID() {
long id = UUID.incrementAndGet();
if (id >= getMaxUUID()) {
synchronized (UUID) {
if (UUID.get() >= id) {
id -= UUID_INTERNAL;
UUID.set(id);
}
}
}
return id;
}
實際測試環境部署了有2個seata-server服務 所以並不能保證 兩個服務產生的branch_id有什么可靠的關聯性,
最終考慮下 比較簡單的方法是在branch_table里增加一個自增的字段,使用該字段代替gmt_create進行上述分支事務查詢的排序依據即可!
(添加了自增的id,之后更改查詢語句如下)

經過更改測試后 發現確實 解決了 上述無法順序回滾帶來的問題!
在測試 時間如果只是啟動一個seata-server服務 一般是不會產生 上述問題的,時間基本 有先后的明顯差別!再者branch_id也是明顯增加的 也可以用作 查詢排序依據,單多台seater-server的時候 就不能保證了!
本地重新打包各模塊后,在seata-server的lib包下替換修改后的新的模塊jar即可!
mvn clean install -DskipTests=true

以上!
