seata tcc模式下的一個性能問題
- 2020 年 1 月 13 日
- 筆記
本文解釋Seata中,AT模式和MT模式下的一個一階段的區別。
根據兩階段行為模式的不同,Seata將分支事務劃分為2種:
- Automatic (Branch) Transaction Mode
- Manual (Branch) Transaction Mode
1.AT模式
AT 模式基於 支援本地 ACID 事務 的 關係型資料庫:
一階段 prepare 行為:在本地事務中,一併提交業務數據更新和相應回滾日誌記錄。 二階段 commit 行為:馬上成功結束,自動 非同步批量清理回滾日誌。 二階段 rollback 行為:通過回滾日誌,自動 生成補償操作,完成數據回滾。
2. MT模式
相應的,MT 模式,不依賴於底層數據資源的事務支援: 一階段 prepare 行為:調用 自定義 的 prepare 邏輯。 二階段 commit 行為:調用 自定義 的 commit 邏輯。 二階段 rollback 行為:調用 自定義 的 rollback 邏輯。 所謂 MT 模式,是指支援把 自定義 的分支事務納入到全局事務的管理中。
3.一階段解讀
在AT模式下,一階段會做如下幾個操作:
1.解析業務sql; 2.獲取sql執行前的鏡像,前鏡像; 3.執行業務sql; 4.獲取sql執行後的鏡像,後鏡像; 5.添加undo_log日誌,把前後鏡像數據和業務sql相關的資訊組成回滾日誌,添加到undo_log中; 6.向TC註冊分支事務,並申請相關目標數據的全局鎖; 7.事務提交,將業務操作和undo_log一起提交; 8.上報分支事務提交結果給TC; 9.釋放本地鎖; 10.釋放資料庫連接;
在AT模式下,一階段,會有如上的多個步驟,以及解析存儲undo_log等操作;那麼,在MT模式中,由於prepare邏輯有對應的rollback邏輯,顯然這裡是不用再添加回滾資訊的。那麼,這MT模式下一階段的處理邏輯,是如何避免上述操作帶來的性能損耗呢?
4.源碼分析
在方法攔截器MethodInterceptor介面下,有一個TCC攔截器實現類TccActionInterceptor,這個實現類有一個invoke方法:
@Override public Object invoke(final MethodInvocation invocation) throws Throwable { if(!RootContext.inGlobalTransaction()){ //not in transaction return invocation.proceed(); } Method method = getActionInterfaceMethod(invocation); TwoPhaseBusinessAction businessAction = method.getAnnotation(TwoPhaseBusinessAction.class); //try method if (businessAction != null) { //save the xid String xid = RootContext.getXID(); //clear the context RootContext.unbind(); try { Object[] methodArgs = invocation.getArguments(); //Handler the TCC Aspect Map<String, Object> ret = actionInterceptorHandler.proceed(method, methodArgs, xid, businessAction, new Callback<Object>() { @Override public Object execute() throws Throwable { return invocation.proceed(); } }); //return the final result return ret.get(Constants.TCC_METHOD_RESULT); } finally { //recovery the context RootContext.bind(xid); } } return invocation.proceed(); }
可以看到,在切面的切入點執行之前,和之後,有2個關鍵操作: 把xid解綁
//save the xid String xid = RootContext.getXID(); //clear the context RootContext.unbind();
恢復xid綁定
//recovery the context RootContext.bind(xid);
這麼做的目的是什麼呢?
當把xid解綁後,tcc的這個prepare分支事務執行時,框架不會攔截業務sql進行解析,也不會存儲前後鏡像和生成undo_log日誌,(即使使用了代理數據源,也不會)這樣,tcc模式下,就避免了一階段的上述操作帶來的損耗。