分支事务的源码主要就是看下面两张图片的内容
看看在第一阶段,RM是如何做的。
DataSourceProxy
Seata它的实现其实是对我们的数据源DataSource进行的代理,在这个过程中进行的一阶段相关的操作
我们的业务sql执行,其实是从DataSourceProxy中拿到ConnectionProxy代理的数据库连接对象,再去拿PreparedStatementProxy对象,再去进行相应的增量逻辑、执行业务sql
所以我们这里就直接从PreparedStatementProxy#execute
方法开始看
微服务端源码
微服务要进行分支事务的注册,那么它要做的事情就分为:
- 解析SQL,判断sql的类型
- 将目标数据库连接对象的自动提交改为false
- 生成before image,加 FOR UPDATE 行锁
- 执行目标sql
- 生成after image
- 生成全局锁lockKeys,用来标记当前操作涉及到了哪个数据表的那些数据。再把前置镜像、后置进行、lockKeys缓存到ConnectionContext对象中
- 通过RM向TC发送分支事务注册的请求
- 在往undo_log数据表中插入数据
- 本地事务的提交
- 将目标数据库连接对象的自动提交改为true
入口是PreparedStatementProxy#execute
。该方法的主要功能为:
- 根据执行sql的类型,创建出对应的Executor对象,并调用execute方法
@Override
public boolean execute() throws SQLException {
// 在ExecuteTemplate.execute()方法中进行相应的方法增强,比如解析sql、生成before image、after image、调用TC注册分支
// statement.execute()就是我们的目标方法执行
return ExecuteTemplate.execute(this, (statement, args) -> statement.execute());
}
// 进入到ExecuteTemplate#execute方法中
public static <T, S extends Statement> T execute(List<SQLRecognizer> sqlRecognizers,
StatementProxy<S> statementProxy,
StatementCallback<T, S> statementCallback,
Object... args) throws SQLException {
// 有没有全局锁、是不是AT模式,如果不满足这些条件那么就直接正常执行目标sql
if (!RootContext.requireGlobalLock() && BranchType.AT != RootContext.getBranchType()) {
return statementCallback.execute(statementProxy.getTargetStatement(), args);
}
String dbType = statementProxy.getConnectionProxy().getDbType();
if (CollectionUtils.isEmpty(sqlRecognizers)) {
sqlRecognizers = SQLVisitorFactory.get(
statementProxy.getTargetSQL(),
dbType);
}
Executor<T> executor;
if (CollectionUtils.isEmpty(sqlRecognizers)) {
executor = new PlainExecutor<>(statementProxy, statementCallback);
} else {
if (sqlRecognizers.size() == 1) {
SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
// SQL执行的类型
switch (sqlRecognizer.getSQLType()) {
case INSERT:
executor = EnhancedServiceLoader.load(InsertExecutor.class, dbType,
new Class[]{StatementProxy.class, StatementCallback.class, SQLRecognizer.class},
new Object[]{statementProxy, statementCallback, sqlRecognizer});
break;
case UPDATE:
// 拿更新操作举例,UpdateExecutor类中就就 beforeImage afterImage相应的方法
executor = new UpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
case DELETE:
executor = new DeleteExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
case SELECT_FOR_UPDATE:
executor = new SelectForUpdateExecutor<>(statementProxy, statementCallback, sqlRecognizer);
break;
case INSERT_ON_DUPLICATE_UPDATE:
switch (dbType) {
case JdbcConstants.MYSQL:
case JdbcConstants.MARIADB:
executor =
new MySQLInsertOnDuplicateUpdateExecutor(statementProxy, statementCallback, sqlRecognizer);
break;
default:
throw new NotSupportYetException(dbType + " not support to INSERT_ON_DUPLICATE_UPDATE");
}
break;
case UPDATE_JOIN:
switch (dbType) {
case JdbcConstants.MYSQL:
executor = new MySQLUpdateJoinExecutor<>(statementProxy,statementCallback,sqlRecognizer);
break;
default:
throw new NotSupportYetException(dbType + " not support to " + SQLType.UPDATE_JOIN.name());
}
break;
default:
executor = new PlainExecutor<>(statementProxy, statementCallback);
break;
}
} else {
executor = new MultiExecutor<>(statementProxy, statementCallback, sqlRecognizers);
}
}
T rs;
try {
// 调用execute方法
rs = executor.execute(args);
} catch (Throwable ex) {
if (!(ex instanceof SQLException)) {
ex = new SQLException(ex);
}
throw (SQLException) ex;
}
return rs;
}
就拿Update语句举例,这里会创建一个UpdateExecutor
对象,并调用executor.execute(args)
方法。我们点进该类就会发现这其中有前置镜像和后置镜像相关的方法,但是没有execute()方法
这里的设计模式是把公共的代码都放在了父类中,各个新增/修改/删除相关的子类只是自己负责前置镜像和后置镜像相关的方法
所以这里会调用至父类BaseTransactionalExecutor#execute
方法中,这一块的总流程为:
-
从
RootContext.getXID()
中取xid,把xid和ConnectionProxy.ConnectionContext对象
进行绑定。在ConnectionProxy事务提交方法中会判断这个xid是否为null -
调用子类的
doExecute()
方法,在该方法中首先将目标数据库连接对象的自动提交改为fals -
调用executeAutoCommitFalse()方法
-
调用子类自己的
beforeImage()
方法,得到beforeImage
-
执行目标sql
-
调用子类自己的
afterImage()
方法,得到afterImage
-
通过调用
prepareUndoLog()
方法将生成一个全局锁lockKeys,主要记录当前更新操作涉及到了哪个数据表,哪些行数据;表名:主键key
再将beforeImage+afterImage封装为一个SQLUndoLog对象,把lockKeys和SQLUndoLog一起保存至
ConnectionContext
对象中
-
-
调用
connectionProxy.commit()
方法- 判断ConnectionContext对象中的 xid != null,进入到下面的流程中;这里还会判断@GlobalLock相关的判断
- 调用
register()
方法,通过RM调用TC,进行分支事务注册 - 再通过
UndoLogManager.flushUndoLogs()
方法 往undo_log数据表插入数据。 - 目标数据库连接对象本地事务提交commit()
- 如果在这个过程中出现了异常,那么就调用
report()
方法,往TC进行上报,并继续往外抛异常
-
最后在finally语句段中清理
ConnectionContext
对象中的数据,再将再把目标连接对象的自动提交设置为true
// BaseTransactionalExecutor#execute
public T execute(Object... args) throws Throwable {
// 从RootContext.getXID() 中取xid
String xid = RootContext.getXID();
if (xid != null) {
// 把xid和ConnectionProxy.ConnectionContext 进行绑定
// 在ConnectionProxy事务提交方法中会判断这个xid是否为null
statementProxy.getConnectionProxy().bind(xid);
}
// 把RootContext.requireGlobalLock()的值存入ConnectionContext对象中
// 在ConnectionProxy事务提交方法中会判断这个值
statementProxy.getConnectionProxy().setGlobalLockRequire(RootContext.requireGlobalLock());
// 调用子类的doExecute()方法
return doExecute(args);
}
// AbstractDMLBaseExecutor#doExecute
public T doExecute(Object... args) throws Throwable {
AbstractConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
if (connectionProxy.getAutoCommit()) {
// 如果目标连接对象的自动提交为true就进入该方法
return executeAutoCommitTrue(args);
} else {
return executeAutoCommitFalse(args);
}
}
// AbstractDMLBaseExecutor#executeAutoCommitTrue
protected T executeAutoCommitTrue(Object[] args) throws Throwable {
ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
try {
// 将目标数据库连接对象的自动提交改为false
connectionProxy.changeAutoCommit();
// 再调用executeAutoCommitFalse()方法,执行业务sql、生成UndoSql对象、生成全局锁Key
// 在调用commit()提交方法,进行分支事务注册,本地事务提交
return new LockRetryPolicy(connectionProxy).execute(() -> {
T result = executeAutoCommitFalse(args);
connectionProxy.commit();
return result;
});
} catch (Exception e) {
// when exception occur in finally,this exception will lost, so just print it here
LOGGER.error("execute executeAutoCommitTrue error:{}", e.getMessage(), e);
if (!LockRetryPolicy.isLockRetryPolicyBranchRollbackOnConflict()) {
connectionProxy.getTargetConnection().rollback();
}
throw e;
} finally {
// 完成后,清理ConnectionContext对象中的数据
connectionProxy.getContext().reset();
// 再把目标连接对象的自动提交设置为true
connectionProxy.setAutoCommit(true);
}
}
//-----------------------------------------------------------------------------------------------
// AbstractDMLBaseExecutor#executeAutoCommitFalse
protected T executeAutoCommitFalse(Object[] args) throws Exception {
try {
// 调用子类自己的beforeImage()方法,得到beforeImage
TableRecords beforeImage = beforeImage();
// 执行目标sql
T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
// 调用子类自己的 afterImage() 方法,得到 afterImage
TableRecords afterImage = afterImage(beforeImage);
// prepareUndoLog()方法只是缓存起来,还没有往DB的undo_log表中插入数据
// 生成一个全局锁的keys,并把 beforeImage 和 afterImage 封装为一个SQLUndoLog对象
// 都保存在connectionProxy.ConnectionContext对象中
prepareUndoLog(beforeImage, afterImage);
return result;
} catch (TableMetaException e) {
LOGGER.error(......);
statementProxy.getConnectionProxy().getDataSourceProxy().tableMetaRefreshEvent();
throw e;
}
}
// BaseTransactionalExecutor#prepareUndoLog
protected void prepareUndoLog(TableRecords beforeImage, TableRecords afterImage) throws SQLException {
if (beforeImage.getRows().isEmpty() && afterImage.getRows().isEmpty()) {
return;
}
if (SQLType.UPDATE == sqlRecognizer.getSQLType()) {
if (beforeImage.getRows().size() != afterImage.getRows().size()) {
throw new ShouldNeverHappenException(...);
}
}
ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
// 取当前操作的image,如果是delete语句就只能用 beforeImage 了,其他情况就用 afterImage
TableRecords lockKeyRecords = sqlRecognizer.getSQLType() == SQLType.DELETE ? beforeImage : afterImage;
// 生成一个全局锁相关的key,内容主要是操作了哪张表的哪些数据,通过主键key来确定
String lockKeys = buildLockKey(lockKeyRecords);
if (null != lockKeys) {
// 把locaKeys保存在connectionProxy.ConnectionContext对象中
connectionProxy.appendLockKey(lockKeys);
// 将 beforeImage 和 afterImage 封装为一个SQLUndoLog对象,并保存在connectionProxy.ConnectionContext对象中
SQLUndoLog sqlUndoLog = buildUndoItem(beforeImage, afterImage);
connectionProxy.appendUndoLog(sqlUndoLog);
}
}
//-----------------------------------------------------------------------------------------------
// ConnectionProxy#commit
public void commit() throws SQLException {
try {
lockRetryPolicy.execute(() -> {
// 进入该方法
doCommit();
return null;
});
} catch (SQLException e) {
//...
} catch (Exception e) {
throw new SQLException(e);
}
}
// 判断是否要进行分支事务注册,如果RootContext.getXID()没有xid的情况下那么就不需要进行分支事务的注册
private void doCommit() throws SQLException {
// 判断ConnectionContext对象中的 xid != null
// 在BaseTransactionalExecutor的execute()方法刚开始就把RootContext.getXID()存入ConnectionContext对象中
if (context.inGlobalTransaction()) {
// 进入该方法,RM调用TC 进行分支注册
processGlobalTransactionCommit();
} else if (context.isGlobalLockRequire()) {
// 再判断isGlobalLockRequire
// 在BaseTransactionalExecutor的execute()方法刚开始就把RootContext.requireGlobalLock()存入ConnectionContext对象中
processLocalCommitWithGlobalLocks();
} else {
// 如果上面的都不满足就直接执行目标sql的提交
targetConnection.commit();
}
}
// 进行分支事务注册、并往undo_log数据表进行insert操作、目标连接对象本地事务提交
private void processGlobalTransactionCommit() throws SQLException {
try {
// 进行分支注册
register();
} catch (TransactionException e) {
recognizeLockKeyConflictException(e, context.buildLockKeys());
}
try {
// UndoLogManager的flushUndoLogs()方法 undo_log数据表插入数据
UndoLogManagerFactory.getUndoLogManager(this.getDbType()).flushUndoLogs(this);
// 目标sql提交
targetConnection.commit();
} catch (Throwable ex) {
LOGGER.error("process connectionProxy commit error: {}", ex.getMessage(), ex);
// 如果出现了异常,那么就上报给TC,然后继续往外抛异常
// TC端就会把当前分支事务对应的状态再数据库中修改一下。在第二阶段全局事务回滚时会用到该状态的判断
report(false);
throw new SQLException(ex);
}
if (IS_REPORT_SUCCESS_ENABLE) {
report(true);
}
context.reset();
}
private void register() throws TransactionException {
// 有没有SQLUndoLog对象,有没有lockKeys全局锁相关的字符串
if (!context.hasUndoLog() || !context.hasLockKey()) {
return;
}
// 调用RM的branchRegister()方法
Long branchId = DefaultResourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(),
null, context.getXid(), context.getApplicationData(),
context.buildLockKeys());
// 将分支事务ID存入ConnectionContext对象中
context.setBranchId(branchId);
}
以修改语句为例,我们来看看详细生成Before Image 和 After Image的代码,在UpdateExecutor
类中
- 首先是生成beforeImage,使用字符串拼接,并在最后添加FOR UPDATE 行锁。
- 然后拿到目标数据库连接对象去执行查询语句
- 再就是afterImage,使用字符串拼接,再一次拿到目标数据库连接对象去执行查询语句
@Override
protected TableRecords beforeImage() throws SQLException {
ArrayList<List<Object>> paramAppenderList = new ArrayList<>();
TableMeta tmeta = getTableMeta();
// 得到查询sql语句
String selectSQL = buildBeforeImageSQL(tmeta, paramAppenderList);
// 去执行sql,这里获取的是目标连接对象执行的sql 查询语句
// 因为如果不是目标连接对象,我这里beforeImage的查询语句加了行锁,真正目标对象再去执行不就锁住了嘛
return buildTableRecords(tmeta, selectSQL, paramAppenderList);
}
private String buildBeforeImageSQL(TableMeta tableMeta, ArrayList<List<Object>> paramAppenderList) {
// 字符串拼接,最终拼成一条select 语句,并在最后加 FOR UPDATE 行锁
SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer) sqlRecognizer;
StringBuilder prefix = new StringBuilder("SELECT ");
StringBuilder suffix = new StringBuilder(" FROM ").append(getFromTableInSQL());
String whereCondition = buildWhereCondition(recognizer, paramAppenderList);
String orderByCondition = buildOrderCondition(recognizer, paramAppenderList);
String limitCondition = buildLimitCondition(recognizer, paramAppenderList);
if (StringUtils.isNotBlank(whereCondition)) {
suffix.append(WHERE).append(whereCondition);
}
if (StringUtils.isNotBlank(orderByCondition)) {
suffix.append(" ").append(orderByCondition);
}
if (StringUtils.isNotBlank(limitCondition)) {
suffix.append(" ").append(limitCondition);
}
suffix.append(" FOR UPDATE");
StringJoiner selectSQLJoin = new StringJoiner(", ", prefix.toString(), suffix.toString());
List<String> needUpdateColumns = getNeedUpdateColumns(tableMeta.getTableName(), sqlRecognizer.getTableAlias(), recognizer.getUpdateColumnsUnEscape());
needUpdateColumns.forEach(selectSQLJoin::add);
return selectSQLJoin.toString();
}
@Override
protected TableRecords afterImage(TableRecords beforeImage) throws SQLException {
TableMeta tmeta = getTableMeta();
if (beforeImage == null || beforeImage.size() == 0) {
return TableRecords.empty(getTableMeta());
}
// 得到查询sql语句
String selectSQL = buildAfterImageSQL(tmeta, beforeImage);
ResultSet rs = null;
// 执行sql,这里也是使用的目标数据库连接对象进行的查询
try (PreparedStatement pst = statementProxy.getConnection().prepareStatement(selectSQL)) {
SqlGenerateUtils.setParamForPk(beforeImage.pkRows(), getTableMeta().getPrimaryKeyOnlyName(), pst);
rs = pst.executeQuery();
return TableRecords.buildRecords(tmeta, rs);
} finally {
IOUtil.close(rs);
}
}
private String buildAfterImageSQL(TableMeta tableMeta, TableRecords beforeImage) throws SQLException {
// 字符串拼接,最终拼成一条select 语句,后置镜像的最后没有 FOR UPDATE 行锁
StringBuilder prefix = new StringBuilder("SELECT ");
String whereSql = SqlGenerateUtils.buildWhereConditionByPKs(tableMeta.getPrimaryKeyOnlyName(), beforeImage.pkRows().size(), getDbType());
String suffix = " FROM " + getFromTableInSQL() + " WHERE " + whereSql;
StringJoiner selectSQLJoiner = new StringJoiner(", ", prefix.toString(), suffix);
SQLUpdateRecognizer recognizer = (SQLUpdateRecognizer) sqlRecognizer;
List<String> needUpdateColumns = getNeedUpdateColumns(tableMeta.getTableName(), sqlRecognizer.getTableAlias(), recognizer.getUpdateColumnsUnEscape());
needUpdateColumns.forEach(selectSQLJoiner::add);
return selectSQLJoiner.toString();
}
TC处理RM分支事务注册请求
我们现在知道了RM要进行分支注册,那么TC这边主要处理流程为:
-
检查当前GlobalSession的action属性是否为true,判断还能不能进行分支事务的注册。当前全局事务的状态如果不为GlobalStatus.Begin 那么也要抛异常
-
创建一个BranchSession对象,这个过程中会生成一个分支事务ID
-
根据branchSession对象,往lock_table全局锁表中插入数据,再插入之前会有一个查询操作,如果全局锁存在冲突则抛异常
-
将全局事务与分支事务进行绑定。往branch_table分支事务表中进行新增insert操作
-
如果往branch_table分支事务表中进行新增insert操作出异常了,那么就要对当前分支事务进行解锁。要去删除lock_table全局锁表中的相应数据
-
返回分支事务ID
通过TCInboundHandler
接口,找处理分支事务注册请求的类,最终定位到DefaultCoordinator#doBranchRegister
方法中。
protected void doBranchRegister(BranchRegisterRequest request, BranchRegisterResponse response,
RpcContext rpcContext) throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
// 进入到core.branchRegister()方法中
response.setBranchId(
core.branchRegister(request.getBranchType(), request.getResourceId(), rpcContext.getClientId(),
request.getXid(), request.getApplicationData(), request.getLockKey()));
}
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid,
String applicationData, String lockKeys) throws TransactionException {
// 继续进入branchRegister()
return getCore(branchType).branchRegister(branchType, resourceId, clientId, xid,
applicationData, lockKeys);
}
// 进入到AbstractCore#branchRegister
@Override
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid,
String applicationData, String lockKeys) throws TransactionException {
GlobalSession globalSession = assertGlobalSessionNotNull(xid, false);
return SessionHolder.lockAndExecute(globalSession, () -> {
// 检查当前GlobalSession的action属性是否为true,判断还能不能进行分支事务的注册
// 当前全局事务的状态如果不为GlobalStatus.Begin 那么也要抛异常
globalSessionStatusCheck(globalSession);
globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
// 创建一个BranchSession对象,这个过程中会生成一个分支事务ID
BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, branchType, resourceId,
applicationData, lockKeys, clientId);
MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(branchSession.getBranchId()));
// 进入该方法,进行该分支事务 处理全局锁的流程
// 往lock_table全局锁表中插入数据
branchSessionLock(globalSession, branchSession);
try {
// 将全局事务与分支事务进行绑定
// 往branch_table分支事务表中进行新增insert操作
globalSession.addBranch(branchSession);
} catch (RuntimeException ex) {
// 如果往branch_table分支事务表中进行新增insert操作出异常了,那么就要对当前分支事务进行解锁
// 要去删除lock_table全局锁表中的相应数据
// 正常解锁流程是在二阶段进行
branchSessionUnlock(branchSession);
throw new BranchTransactionException(...);
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Register branch successfully, xid = {}, branchId = {}, resourceId = {} ,lockKeys = {}",
globalSession.getXid(), branchSession.getBranchId(), resourceId, lockKeys);
}
return branchSession.getBranchId();
});
}
生成全局锁相关的流程
//ATCore#branchSessionLock
protected void branchSessionLock(GlobalSession globalSession, BranchSession branchSession)
throws TransactionException {
String applicationData = branchSession.getApplicationData();
boolean autoCommit = true;
boolean skipCheckLock = false;
if (StringUtils.isNotBlank(applicationData)) {
try {
Map<String, Object> data = objectMapper.readValue(applicationData, HashMap.class);
Object clientAutoCommit = data.get(AUTO_COMMIT);
if (clientAutoCommit != null && !(boolean)clientAutoCommit) {
autoCommit = (boolean)clientAutoCommit;
}
Object clientSkipCheckLock = data.get(SKIP_CHECK_LOCK);
if (clientSkipCheckLock instanceof Boolean) {
skipCheckLock = (boolean)clientSkipCheckLock;
}
} catch (IOException e) {
LOGGER.error("failed to get application data: {}", e.getMessage(), e);
}
}
try {
// 进入branchSession.lock()方法中
// 往lock_table数据表中insert。如果lock()方法了false,那么这里就要抛异常了
if (!branchSession.lock(autoCommit, skipCheckLock)) {
throw new BranchTransactionException(...);
}
} catch (StoreException e) {
//...
throw e;
}
}
// BranchSession#lock
public boolean lock(boolean autoCommit, boolean skipCheckLock) throws TransactionException {
// 必须是AT模式
if (this.getBranchType().equals(BranchType.AT)) {
// 进入acquireLock 往lock_table数据表中insert
return LockerManagerFactory.getLockManager().acquireLock(this, autoCommit, skipCheckLock);
}
return true;
}
// AbstractLockManager#acquireLock
public boolean acquireLock(BranchSession branchSession, boolean autoCommit, boolean skipCheckLock) throws TransactionException {
if (branchSession == null) {
throw new IllegalArgumentException("branchSession can't be null for memory/file locker.");
}
String lockKey = branchSession.getLockKey();
if (StringUtils.isNullOrEmpty(lockKey)) {
return true;
}
// collectRowLocks(branchSession)方法,将当前分支事务的lockKeys字符串进行解析,封装为List<RowLock>对象
List<RowLock> locks = collectRowLocks(branchSession);
if (CollectionUtils.isEmpty(locks)) {
return true;
}
// 调用acquireLock()方法
// 往lock_table数据表中insert
return getLocker(branchSession).acquireLock(locks, autoCommit, skipCheckLock);
}
// DataBaseLocker#acquireLock
public boolean acquireLock(List<RowLock> locks, boolean autoCommit, boolean skipCheckLock) {
if (CollectionUtils.isEmpty(locks)) {
// no lock
return true;
}
try {
// 进入acquireLock()
// 全局锁的插入操作,往lock_table数据表中insert
return lockStore.acquireLock(convertToLockDO(locks), autoCommit, skipCheckLock);
} catch (StoreException e) {
throw e;
} catch (Exception t) {
LOGGER.error("AcquireLock error, locks:{}", CollectionUtils.toString(locks), t);
return false;
}
}
// LockStoreDataBaseDAO#acquireLock
public boolean acquireLock(List<LockDO> lockDOs, boolean autoCommit, boolean skipCheckLock) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> dbExistedRowKeys = new HashSet<>();
boolean originalAutoCommit = true;
if (lockDOs.size() > 1) {
lockDOs = lockDOs.stream().filter(LambdaUtils.distinctByKey(LockDO::getRowKey)).collect(Collectors.toList());
}
try {
conn = lockStoreDataSource.getConnection();
if (originalAutoCommit = conn.getAutoCommit()) {
conn.setAutoCommit(false);
}
List<LockDO> unrepeatedLockDOs = lockDOs;
//check lock
if (!skipCheckLock) {
boolean canLock = true;
//query
// 先进行锁的检查,判断是否全局锁冲突
String checkLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getCheckLockableSql(lockTable, lockDOs.size());
ps = conn.prepareStatement(checkLockSQL);
for (int i = 0; i < lockDOs.size(); i++) {
ps.setString(i + 1, lockDOs.get(i).getRowKey());
}
rs = ps.executeQuery();
String currentXID = lockDOs.get(0).getXid();
boolean failFast = false;
while (rs.next()) {
String dbXID = rs.getString(ServerTableColumnsName.LOCK_TABLE_XID);
// 下面这个if如果满足则表示全局锁冲突
if (!StringUtils.equals(dbXID, currentXID)) {
if (LOGGER.isInfoEnabled()) {
String dbPk = rs.getString(ServerTableColumnsName.LOCK_TABLE_PK);
String dbTableName = rs.getString(ServerTableColumnsName.LOCK_TABLE_TABLE_NAME);
long dbBranchId = rs.getLong(ServerTableColumnsName.LOCK_TABLE_BRANCH_ID);
LOGGER.info(...);
}
if (!autoCommit) {
int status = rs.getInt(ServerTableColumnsName.LOCK_TABLE_STATUS);
if (status == LockStatus.Rollbacking.getCode()) {
failFast = true;
}
}
canLock = false;
break;
}
dbExistedRowKeys.add(rs.getString(ServerTableColumnsName.LOCK_TABLE_ROW_KEY));
}
// 全局锁冲突 抛异常
if (!canLock) {
conn.rollback();
if (failFast) {
throw new StoreException(new BranchTransactionException(LockKeyConflictFailFast));
}
return false;
}
// If the lock has been exists in db, remove it from the lockDOs
if (CollectionUtils.isNotEmpty(dbExistedRowKeys)) {
unrepeatedLockDOs = lockDOs.stream().filter(lockDO -> !dbExistedRowKeys.contains(lockDO.getRowKey()))
.collect(Collectors.toList());
}
if (CollectionUtils.isEmpty(unrepeatedLockDOs)) {
conn.rollback();
return true;
}
}
// lock
// 再调用doAcquireLock()方法进行全局锁的插入操作,往lock_table数据表中insert
// 如果插入失败,那么这里就回滚,并返回false,在外层方法调用的位置中进行boolean的值判断进行抛异常
if (unrepeatedLockDOs.size() == 1) {
LockDO lockDO = unrepeatedLockDOs.get(0);
if (!doAcquireLock(conn, lockDO)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global lock acquire failed, xid {} branchId {} pk {}", ...);
}
conn.rollback();
return false;
}
} else {
if (!doAcquireLocks(conn, unrepeatedLockDOs)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global lock batch acquire failed, xid {} branchId {} pks {}", ...);
}
conn.rollback();
return false;
}
}
conn.commit();
return true;
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(rs, ps);
if (conn != null) {
try {
if (originalAutoCommit) {
conn.setAutoCommit(true);
}
conn.close();
} catch (SQLException e) {
}
}
}
}
将全局事务与分支事务进行绑定, 往branch_table分支事务表中进行新增insert操作的流程
// GlobalSession#addBranch
public void addBranch(BranchSession branchSession) throws TransactionException {
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
// 进入该方法 往branch_table分支事务表中进行新增insert操作
lifecycleListener.onAddBranch(this, branchSession);
}
// 修改分支事务状态
branchSession.setStatus(BranchStatus.Registered);
// 往集合中存当前branchSession对象
add(branchSession);
}
// AbstractSessionManager#onAddBranch
public void onAddBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
// 进入该方法
// 各个子类会重写下面的方法 DB 、 redis 相关的实现类会重写addBranchSession()方法
// 往branch_table分支事务表中进行新增insert操作
addBranchSession(globalSession, branchSession);
}
// DataBaseSessionManager#addBranchSession
public void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException {
if (StringUtils.isNotBlank(taskName)) {
return;
}
// 往branch_table分支事务表中进行新增insert操作
boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_ADD, session);
if (!ret) {
throw new StoreException("addBranchSession failed.");
}
}