用Java輕松完成一個分布式事務TCC,自動處理空補償、懸掛、冪等
作者:葉東富
來源:SegmentFault 思否社區(qū)
處理NPC
分布式系統最大的敵人可能就是NPC了,在這里它是Network Delay, Process Pause, Clock Drift的首字母縮寫。我們先看看具體的NPC問題是什么:
Network Delay,網絡延遲。雖然網絡在多數情況下工作的還可以,雖然TCP保證傳輸順序和不會丟失,但它無法消除網絡延遲問題。
Process Pause,進程暫停。有很多種原因可以導致進程暫停:比如編程語言中的GC(垃圾回收機制)會暫停所有正在運行的線程;再比如,我們有時會暫停云服務器,從而可以在不重啟的情況下將云服務器從一臺主機遷移到另一臺主機。我們無法確定性預測進程暫停的時長,你以為持續(xù)幾百毫秒已經很長了,但實際上持續(xù)數分鐘之久進程暫停并不罕見。
Clock Drift,時鐘漂移?,F實生活中我們通常認為時間是平穩(wěn)流逝,單調遞增的,但在計算機中不是。計算機使用時鐘硬件計時,通常是石英鐘,計時精度有限,同時受機器溫度影響。為了在一定程度上同步網絡上多個機器之間的時間,通常使用NTP協議將本地設備的時間與專門的時間服務器對齊,這樣做的一個直接結果是設備的本地時間可能會突然向前或向后跳躍。
分布式數據庫內部的分布式事務:NPC中的C給這類應用,帶來了極大的挑戰(zhàn),例如Spanner采用了原子鐘,并且增加了Commit-Wait來解決問題。TiDB采用單點授時,引入了一個單點。
跨數據庫的異構分布式事務:這是我們這篇文章討論的主題,因為沒有涉及時間戳,所以NPC帶來的困擾主要是NP。
TCC的空補償與懸掛
空補償:Cancel執(zhí)行時,Try未執(zhí)行,事務分支的Cancel操作需要判斷出Try未執(zhí)行,這時需要忽略Cancel中的業(yè)務數據更新,直接返回
懸掛:Try執(zhí)行時,Cancel已執(zhí)行完成,事務分支的Try操作需要判斷出Cancel一致性,這時需要忽略Try中的業(yè)務數據更新,直接返回
現有方案的問題
開源項目dtm地址:
https://github.com/yedf/dtm
空補償:“針對該問題,在服務設計時,需要允許空補償,即在沒有找到要補償的業(yè)務主鍵時,返回補償成功,并將原業(yè)務主鍵記錄下來,標記該業(yè)務流水已補償成功。”
防懸掛:“需要檢查當前業(yè)務主鍵是否已經在空補償記錄下來的業(yè)務主鍵中存在,如果存在則要拒絕執(zhí)行該筆服務,以免造成數據不一致。”
正常執(zhí)行順序下,Try執(zhí)行時,在查完沒有空補償記錄的業(yè)務主鍵之后,事務提交之前,如果發(fā)生了進程暫停P,或者事務內部進行網絡請求出現了擁塞,導致本地事務等待較久
全局事務超時后,Cancel執(zhí)行,因為沒有查到要補償的業(yè)務主鍵,因此判斷是空補償,直接返回
Try的進程暫停結束,最后提交本地事務
全局事務回滾完成后,Try分支的業(yè)務操作沒有被回滾,產生了懸掛
子事務屏障技術
在本地數據庫中創(chuàng)建好子事務屏障表dtm_barrier.barrier,唯一索引為gid-branchid-branchop
對于Try、Confirm、Cancel操作,insert ignore一條記錄gid-branchid-try|confirm|cancel,如果影響行數為0(重復請求、懸掛),直接提交返回
對于Cancel操作額外再insert ingore一條記錄 gid-branchid-try,如果影響行數為1(空補償),直接提交返回
執(zhí)行業(yè)務邏輯并提交返回,如果業(yè)務發(fā)生錯誤則回滾
情況1,Try插入gid-branchid-try失敗,Cancel操作插入gid-branchid-try成功,此時就是典型的空補償和懸掛場景,按照子事務屏障算法,Try和Cancel都會直接返回
情況2,Try插入gid-branchid-try成功,Cancel操作插入gid-branchid-try失敗,按照上述子事務屏障算法,會正常執(zhí)行業(yè)務,而且業(yè)務執(zhí)行的順序是Try在Cancel前
情況3,Try和Cancel的操作在重疊期間又遇見宕機等情況,那么至少Cancel會被dtm重試,那么最終會走到情況1或2。
兩個insert判斷解決空補償、防懸掛、冪等這三個問題,比其他方案的三種情況分別判斷,邏輯復雜度大幅降低
dtm的子事務屏障是SDK層解決這三個問題,業(yè)務完全不需要關心
性能高,對于正常完成的事務(一般失敗的事務不超過1%),子事務屏障的額外開銷是每個分支操作一個SQL,比其他方案代價更小。
Java接入

