一信粮、seata是什么
Seata 是一款開源的分布式事務(wù)解決方案,致力于提供高性能和簡單易用的分布式事務(wù)服務(wù)梦皮。Seata 將為用戶提供了 AT炭分、TCC、SAGA 和 XA 事務(wù)模式剑肯,為用戶打造一站式的分布式解決方案捧毛。
在19年初的時候就關(guān)注過這個中間件(當(dāng)時叫Fescar),并且對它的源碼進行了一下分析--阿里分布式事務(wù)解決方案fescar簡析让网。然而當(dāng)時并不成熟呀忧,并不能直接用于商用,主要有以下幾個問題溃睹。
- TC的實現(xiàn)不完善而账。不支持HA,xid的生成因篇,session的存儲泞辐,鎖的實現(xiàn)等都是以DEMO的方式提供,不能直接用于線上環(huán)境竞滓,需要二次開發(fā)
- 回滾失敗的補償機制不完善
- 性能問題咐吼。seata當(dāng)時宣稱只有在第一階段提交的時候進行加鎖,相對傳統(tǒng)的兩階段提交對性能有比較大的提升商佑。但現(xiàn)實情況是為了保證回滾操作的成功锯茄,還必須要有一個全局鎖,事實上相比XA的方式我個人認為在系統(tǒng)的吞吐量上個人認為不會有太大的變化茶没。而且由于這種方式實現(xiàn)的是類似補償性事務(wù)的方式肌幽,又會引入一個可見性的問題(第二階段提交前就已經(jīng)能看到第一階段提交的結(jié)果)
- RPC框架。由于是阿里系的中間件抓半,因此第一版實現(xiàn)的是基于dubbo喂急,非dubbo的rpc框架需要根據(jù)自己的情況做二次開發(fā)。
二笛求、模式
- AT
- TCC
- SAGA(新特性)
- XA(新特性)
三煮岁、核心組件
- TC
事務(wù)協(xié)調(diào)者
- TM
事務(wù)發(fā)起者讥蔽。定義事務(wù)邊界
- RM
事務(wù)參與者
四、demo演示
五画机、源碼分析
注:本文使用的版本為v1.2.0
1. undo_log的產(chǎn)生和刪除機制
- undo_log的產(chǎn)生
查看flushUndoLogs調(diào)用棧
flushUndoLogs:200, AbstractUndoLogManager (io.seata.rm.datasource.undo)
processGlobalTransactionCommit:221, ConnectionProxy (io.seata.rm.datasource)
doCommit:196, ConnectionProxy (io.seata.rm.datasource)
lambda$commit$0:184, ConnectionProxy (io.seata.rm.datasource)
call:-1, 362578118 (io.seata.rm.datasource.ConnectionProxy$$Lambda$197)
execute:289, ConnectionProxy$LockRetryPolicy (io.seata.rm.datasource)
commit:183, ConnectionProxy (io.seata.rm.datasource)
...
execute:108, ExecuteTemplate (io.seata.rm.datasource.exec)
execute:49, ExecuteTemplate (io.seata.rm.datasource.exec)
...
update:927, JdbcTemplate (org.springframework.jdbc.core)
deduct:51, StorageServiceImpl (io.seata.samples.dubbo.service.impl)
ConnectionProxy為Connection的代理類。
private void processGlobalTransactionCommit() throws SQLException {
try {
//**新特性新症,分支事務(wù)注冊到TC改為在提交前進行步氏,而不是在一開始就獲取一個branchId
register();
} catch (TransactionException e) {
recognizeLockKeyConflictException(e, context.buildLockKeys());
}
try {
//根據(jù)數(shù)據(jù)庫的類型獲取對應(yīng)的UndoLogManager進行刷寫undolog日志
UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this);
//原connection的commit操作
targetConnection.commit();
} catch (Throwable ex) {
LOGGER.error("process connectionProxy commit error: {}", ex.getMessage(), ex);
report(false);
throw new SQLException(ex);
}
if (IS_REPORT_SUCCESS_ENABLE) {
report(true);
}
context.reset();
}
AbstractUndoLogManager.java
@Override
public void flushUndoLogs(ConnectionProxy cp) throws SQLException {
//通過連接代理獲取連接的上下文,這里先不分析xid的傳遞機制徒爹,留給后面的部分進行分析
ConnectionContext connectionContext = cp.getContext();
if (!connectionContext.hasUndoLog()) {
return;
}
String xid = connectionContext.getXid();
long branchId = connectionContext.getBranchId();
BranchUndoLog branchUndoLog = new BranchUndoLog();
branchUndoLog.setXid(xid);
branchUndoLog.setBranchId(branchId);
//具體的undolog的內(nèi)容
branchUndoLog.setSqlUndoLogs(connectionContext.getUndoItems());
UndoLogParser parser = UndoLogParserFactory.getInstance();
byte[] undoLogContent = parser.encode(branchUndoLog);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Flushing UNDO LOG: {}", new String(undoLogContent, Constants.DEFAULT_CHARSET));
}
//實際的寫入操作荚醒,不同的關(guān)系型數(shù)據(jù)庫有不同的實現(xiàn)
insertUndoLogWithNormal(xid, branchId, buildContext(parser.getName()), undoLogContent,
cp.getTargetConnection());
}
為了直觀顯示,我這里給出了一條undo_log的數(shù)據(jù)
id: 32
branch_id: 2011290555
xid: 172.17.0.1:8091:2011290554
context: serializer=jackson
rollback_info: {"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"172.17.0.1:8091:2011290554","branchId":2011290555,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"UPDATE","tableName":"storage_tbl","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"storage_tbl","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":4},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":201}]]}]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"storage_tbl","rows":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Row","fields":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"id","keyType":"PRIMARY_KEY","type":4,"value":4},{"@class":"io.seata.rm.datasource.sql.struct.Field","name":"count","keyType":"NULL","type":4,"value":199}]]}]]}}]]}
log_status: 0
log_created: 2020-05-10 10:02:53
log_modified: 2020-05-10 10:02:53
ext: NULL
這個操作為把用戶的庫存記錄-2隆嗅。beforeImage的count為201界阁,afterImage的count為199。
- RPC框架整合(xid傳遞)
- 鎖的機制(全局鎖和局部鎖)
- session狀態(tài)存儲
- 全局ID生成