Seata源码分析:分支事务源码



前文 Seata全局事务源码分析


分支事务的源码主要就是看下面两张图片的内容

在这里插入图片描述

在这里插入图片描述

看看在第一阶段,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.");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值