• Home
  • Articles
    • 日志
    • 妍小言
    • 舒小书
    • 浩然说
    • 生活日记
  • All Tags

Seata

27 Jun 2019

Reading time ~1 minute

Seata是阿里巴巴开源的分布式事务中间件,以高效并且对业务0侵入的方式,解决微服务场景下面临的分布式事务问题。

Seata中的几个组建

  • Server,维护全局事务和各个分支的会话信息并保证全局事务之间的隔离性,协调分支事务提交或者回滚
  • TM,全局事务的实例,定义全局事务的范围,事务的传递,并发起全局事务的提交与回滚
  • RM,分支事务,维护整个链路中一环的提交与回滚,并上报分支事务状态

Seata设计思路

Seata的设计思路继承自二阶段提交,即全局事务的提交分成两个阶段,第一阶段,注册分支事务,执行SQL,上报分支事务状态,第二阶段,根据TC Commit/Rollback指令,提交/回滚本地事务。

Seata的设计与典型的二阶段提交有所不同,在第一阶段,本地SQL事务执行成功,就会直接commit,并不会推迟到第二阶段才提交,全局事务仍然是到第二阶段才进行提交。 本地事务在第一阶段,会根据事务执行前后的数据库快照,生成一个undolog。

    protected T executeAutoCommitFalse(Object[] args) throws Throwable {
        TableRecords beforeImage = beforeImage();
        T result = statementCallback.execute(statementProxy.getTargetStatement(), args);
        TableRecords afterImage = afterImage(beforeImage);
        prepareUndoLog(beforeImage, afterImage);
        return result;
    }

第二阶段,如果全局事务执行Commit操作,分支事务接收到指令,则无需再做任何本地提交操作,直接异步返回成功即可;如果全局事务执行Rollback操作,分支事务接收到指令,根据undolog的数据,对本地事务进行回滚操作。

默认情况下,Seata的本地事务隔离级别依据数据库配置决定,如读已提交,而全局事务的隔离级别为读未提交。如果业务上强制要求全局事务同样是读已提交,Seata已经对select for update做了处理,使用该语法,会要求获取全局事务锁,利用全局事务锁来保证达到读已提交。

对Seata认识

有利的方面

  1. 对二阶段提交进行了优化,在一阶段就进行本地事务提交操作,减少了对资源的占用。
  2. 设计方案未以来数据库本身的支持,为更多的场景的接入提供可能性
  3. 全局事务锁的设计依赖主键,即行级锁,相比于mysql InnoDB的锁索引,锁的粒度更小
    /**
     * build lockKey
     *
     * @param rowsIncludingPK the records
     * @return the string
     */
    protected String buildLockKey(TableRecords rowsIncludingPK) {
        if (rowsIncludingPK.size() == 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(rowsIncludingPK.getTableMeta().getTableName());
        sb.append(":");
        int filedSequence = 0;
        for (Field field : rowsIncludingPK.pkRows()) {
            sb.append(field.getValue());
            filedSequence++;
            if (filedSequence < rowsIncludingPK.pkRows().size()) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

一些限制

  1. 默认情况下的全局事务隔离级别,可能会导致业务上的脏数据的产生。
  2. TM严格依赖分支的失败异常通知,对于某些依赖错误码通知的场景不友好。
  3. 全局事务锁在某些场景下仍然会限制本地分支资源的释放,如:事务B完成一阶段的前提是获取全局事务锁, 如果事务A和事务B有相同的行操作并且事务A二阶段暂未完成,则全局事务锁不会被释放,事务B可能在一阶段被挂起或者获取不到全局事务锁而产生异常。


seatadistributed transaction