那些年背过的题:Spring事务-事务隔离级别底层实现原理

291 阅读9分钟

Spring事务隔离级别是通过底层数据库的事务管理机制来实现的。事务隔离级别定义了在事务并发执行时,一个事务所做的改变对其他事务的可见性。Spring框架本身不直接实现这些隔离级别,而是利用底层数据库(如MySQL、PostgreSQL、Oracle等)的支持。下面是一些关键概念和实现方式:

  1. 事务隔离级别

    • READ_UNCOMMITTED(未提交读) :最低的隔离级别,允许读取未提交的数据,可能导致脏读、不可重复读和幻读。
    • READ_COMMITTED(已提交读) :只读取已提交的数据,可以防止脏读,但不能防止不可重复读和幻读。
    • REPEATABLE_READ(可重复读) :确保在同一个事务中多次读取时数据是一致的,可以防止脏读和不可重复读,但不能防止幻读。
    • SERIALIZABLE(可串行化) :最高的隔离级别,通过强制事务顺序执行来防止脏读、不可重复读和幻读。
  2. Spring中的配置: 在Spring中,你可以通过@Transactional注解来设置事务的隔离级别。例如:

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void someTransactionalMethod() {
        // 方法实现
    }
    
  3. 底层实现: Spring的事务管理器(如DataSourceTransactionManager)会将配置的隔离级别传递到底层数据库驱动。具体步骤如下:

    • 当事务开始时,Spring的事务管理器会通过JDBC连接设置事务的隔离级别。
    • 这通常是通过标准的JDBC API完成的,例如调用Connection.setTransactionIsolation(int level)方法。
    • 底层数据库接收到这个请求后,会根据自身的实现调整隔离级别。例如,在MySQL中,这可以通过SET TRANSACTION ISOLATION LEVEL SQL语句来实现。
  4. 数据库支持: 不同的数据库对事务隔离级别的支持和实现方式可能有所不同。一般来说,关系型数据库都会遵循SQL标准,但具体的性能优化和实现细节会有所差异。例如:

    • MySQL使用InnoDB存储引擎时,支持所有标准的事务隔离级别,并可以通过配置文件或运行时设置进行调整。
    • PostgreSQL也提供了全面的事务隔离级别支持,并在其MVCC(多版本并发控制)机制下有详细的实现。

AbstractPlatformTransactionManager核心源码分析

AbstractPlatformTransactionManager 是 Spring 事务管理框架中的一个核心抽象类,提供了事务处理的基本骨架。许多具体的事务管理器(如 DataSourceTransactionManager)都继承自这个抽象类,并实现了各自特定的事务处理逻辑。在这个抽象类中,定义了一系列模板方法和基本的事务控制逻辑。

核心方法解析

1. getTransaction()

负责获取当前事务状态或创建一个新的事务。

@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    Object transaction = doGetTransaction();

    // Determine if we need to start a new transaction.
    boolean debugEnabled = logger.isDebugEnabled();
    if (definition == null) {
        definition = new DefaultTransactionDefinition();
    }

    if (isExistingTransaction(transaction)) {
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }

    if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
    }

    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException(
                "No existing transaction found for transaction marked with propagation 'mandatory'");
    }

    AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = suspend(null);

    try {
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        DefaultTransactionStatus status = newTransactionStatus(
                definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        doBegin(transaction, definition);
        prepareSynchronization(status, definition);
        return status;
    } catch (RuntimeException | Error ex) {
        resume(null, suspendedResources);
        throw ex;
    }
}
  • 获取事务对象:调用 doGetTransaction() 方法获取当前的事务对象。
  • 判断是否存在事务:检查事务对象是否已经存在事务,通过 isExistingTransaction(transaction) 方法。
  • 处理现有事务:如果存在事务,则通过 handleExistingTransaction() 方法处理。
  • 启动新事务:如果没有现有事务,根据事务定义启动一个新事务,包括设置超时、挂起现有事务等。

2. commit()

负责提交事务。

@Override
public final void commit(TransactionStatus status) throws TransactionException {
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException(
                "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    processCommit(defStatus);
}
  • 检查事务状态:确保事务当前是未完成状态,否则抛出异常。
  • 调用 processCommit 方法:处理事务提交的具体逻辑。
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
    //初始化标志变量 `beforeCompletionInvoked`,用于跟踪是否已经触发了完成前处理(`beforeCompletion`)
        boolean beforeCompletionInvoked = false; 

        try {
        // 初始化标志变量 `unexpectedRollback`,用于标记是否发生了意外的回滚。
            boolean unexpectedRollback = false;   
            prepareForCommit(status);  //为提交事务做准备。这通常用于检查和设置一些需要提交的资源或状态。
            triggerBeforeCommit(status); //通知所有已注册的事务同步器(`TransactionSynchronization`)即将提交事务
            triggerBeforeCompletion(status); //通知所有已注册的事务同步器即将完成事务
            beforeCompletionInvoked = true; //设置为 `true`,表示已经触发了完成前处理
 
            if (status.hasSavepoint()) {     //如果事务有保存点(`savepoint`),则释放保存点。
                status.releaseHeldSavepoint(); 
            } else if (status.isNewTransaction()) { //如果是一个新事务,调用 `doCommit(status)` 方法执行实际的提交操作。
                doCommit(status);
            } else if (isFailEarlyOnGlobalRollbackOnly()) {  //如果配置了全局回滚检查并且需要提前失败,则标记 `unexpectedRollback` 为 `true`
                unexpectedRollback = true;
            } else if (status.isGlobalRollbackOnly()) {   //如果事务被标记为仅回滚(`globalRollbackOnly`),则标记 `unexpectedRollback` 为 `true`
                unexpectedRollback = true;
            }
// 如果没有发生意外的回滚,调用 `triggerAfterCommit(status)` 方法,触发提交后的处理。
            if (!unexpectedRollback) {
            //通知所有已注册的事务同步器事务已经成功提交。
                triggerAfterCommit(status);
            }
        } catch (UnexpectedRollbackException ex) {
        //通知所有已注册的事务同步器事务已经完成,无论是提交还是回滚。
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
            throw ex;
        } catch (TransactionException ex) {
            if (beforeCompletionInvoked) {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
            }
            throw ex;
        }

        triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
    } finally {
    //-   清理事务完成后的资源,包括重置事务状态、解除资源绑定以及恢复挂起的事务等。
        cleanupAfterCompletion(status);
    }
}
  • 准备提交:调用 prepareForCommit(status) 方法。
  • 触发提交前事件:包括 triggerBeforeCommit(status) 和 triggerBeforeCompletion(status)
  • 执行提交:调用 doCommit(status) 方法。
  • 触发提交后事件:包括 triggerAfterCommit(status) 和 triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED)
  • 清理工作:调用 cleanupAfterCompletion(status) 方法。
异常处理
  • UnexpectedRollbackException:如果捕获到意外回滚异常(UnexpectedRollbackException),将 unexpectedRollback 标记为 true 并重新抛出该异常。
  • TransactionException:如果捕获到一般的事务异常(TransactionException),检查是否已经触发了完成前处理,如果是,则调用 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN) 处理完成后逻辑,并重新抛出异常。
  • RuntimeException 或 Error:捕获运行时异常或错误,同样检查是否已经触发了完成前处理,如果是,则调用 triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN) 处理完成后逻辑,并重新抛出异常。

3. rollback()

负责回滚事务。

@Override
public final void rollback(TransactionStatus status) throws TransactionException {
    if (status.isCompleted()) {
        throw new IllegalTransactionStateException(
                "Transaction is already completed - do not call commit or rollback more than once per transaction");
    }

    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    processRollback(defStatus);
}
  • 检查事务状态:确保事务当前是未完成状态,否则抛出异常。
  • 调用 processRollback 方法处理具体的回滚逻辑
private void processRollback(DefaultTransactionStatus status, boolean unexpected) throws TransactionException {
    try {
        boolean unexpectedRollback = unexpected;

        try {
            triggerBeforeCompletion(status);

            if (status.hasSavepoint()) {
                status.rollbackToHeldSavepoint();
            } else if (status.isNewTransaction()) {
                doRollback(status);
            } else {
                if (status.hasTransaction()) {
                    if (status.isGlobalRollbackOnly() || unexpectedRollback) {
                        doSetRollbackOnly(status);
                    }
                } else {
                    logger.debug("Should roll back but has no transaction.");
                }
            }

        } catch (UnexpectedRollbackException ex) {
            unexpectedRollback = true;
            throw ex;
        } catch (TransactionException ex) {
            if (status.isDebug()) {
                logger.debug("Transactional code threw unexpected exception", ex);
            }
            throw ex;
        }

        triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    } finally {
        cleanupAfterCompletion(status);
    }
}
  • 触发提交前事件:调用 triggerBeforeCompletion(status) 函数。

  • 执行回滚

    • 保存点回滚:如果有保存点,调用 rollbackToHeldSavepoint() 方法回滚到保存点。
    • 新事务回滚:如果是一个新的事务,通过 doRollback(status) 进行回滚。
    • 全局回滚:如果是全局回滚或意外回滚,通过 doSetRollbackOnly(status) 设置为仅回滚。
  • 触发回滚后事件:调用 triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK) 函数。

  • 清理工作:调用 cleanupAfterCompletion(status) 函数。

模板方法概述

AbstractPlatformTransactionManager 使用了模板方法模式,定义了一些抽象方法,让子类来实现具体的事务操作逻辑。主要有以下几个:

  1. doGetTransaction() 获取事务对象。由子类实现,用于返回实际的事务对象。
  2. doBegin(Object transaction, TransactionDefinition definition) 开始事务。由子类实现,用于根据事务定义启动一个新事务。
  3. doCommit(DefaultTransactionStatus status) 提交事务。由子类实现,执行事务提交的实际逻辑。
  4. doRollback(DefaultTransactionStatus status) 回滚事务。由子类实现,执行事务回滚的实际逻辑。
  5. doSetRollbackOnly(DefaultTransactionStatus status) 设置事务为仅回滚状态。由子类实现,当需要标记事务只能回滚时调用。

事务同步机制

AbstractPlatformTransactionManager 还提供了事务同步机制,可以在事务生命周期内绑定和管理资源:

  • prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) 准备事务同步,注册事务同步器。
  • triggerBeforeCommit(DefaultTransactionStatus status) 在事务提交前触发事务同步器中的相应方法。
  • triggerBeforeCompletion(DefaultTransactionStatus status) 在事务完成前触发事务同步器中的相应方法。
  • triggerAfterCommit(DefaultTransactionStatus status) 在事务提交后触发事务同步器中的相应方法。
  • triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) 在事务完成后触发事务同步器中的相应方法,无论是提交还是回滚。

DataSourceTransactionManager核心源码分析

DataSourceTransactionManager 是 Spring 框架中用于管理 JDBC 事务的核心组件。它主要负责协调数据库连接、定义事务边界以及处理事务的提交和回滚。下面我们通过分析其核心源码,详细解释其工作原理。

1. doBegin 方法

doBegin 方法负责开始一个新的事务,它会获取数据库连接并设置事务的必要属性,如隔离级别、只读状态等。

@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    Connection con = null;

    try {
        // 如果当前事务没有连接持有者(即当前事务对象还没有绑定数据库连接),或者连接持有者已经同步到事务(说明可能处于重入状态,或其他特殊情况需要重新获取连接)。
        if (!txObject.hasConnectionHolder() || 
            txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            Connection newCon = obtainDataSource().getConnection();
            if (logger.isDebugEnabled()) {
                logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
            }
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }

// 将 `ConnectionHolder` 与当前事务同步,并从 `ConnectionHolder` 中获取实际的数据库连接赋值给 `con`。
        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();

        // 设置事务的隔离级别,并保存之前的隔离级别
        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);

        // 设置只读属性
        if (definition.isReadOnly()) {
            try {
                con.setReadOnly(true);
            } catch (SQLException ex) {
                logger.debug("Could not set JDBC Connection read-only", ex);
            }
        }

        // 设置事务的超时时间
        if (txObject.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
            txObject.getConnectionHolder().setTimeoutInSeconds(txObject.getTimeout());
        }

        // 将连接持有者绑定到当前线程
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
        }
    } catch (Throwable ex) {
        if (txObject.isNewConnectionHolder()) {
            DataSourceUtils.releaseConnection(con, obtainDataSource());
            txObject.setConnectionHolder(null, false);
        }
        throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
    }
}

2. doCommit 方法

doCommit 方法负责提交事务。

@Override
protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();

    if (status.isDebug()) {
        logger.debug("Committing JDBC transaction on Connection [" + con + "]");
    }

    try {
        // 提交 JDBC 事务
        con.commit();
    } catch (SQLException ex) {
        throw new TransactionSystemException("Could not commit JDBC transaction", ex);
    }
}

3. doRollback 方法

doRollback 方法负责回滚事务。

@Override
protected void doRollback(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();

    if (status.isDebug()) {
        logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
    }

    try {
        // 回滚 JDBC 事务
        con.rollback();
    } catch (SQLException ex) {
        throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
    }
}

4. doCleanupAfterCompletion 方法

doCleanupAfterCompletion 方法在事务完成后清理资源。

@Override
protected void doCleanupAfterCompletion(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

    // 从当前线程解绑连接持有者
    if (txObject.isNewConnectionHolder()) {
        TransactionSynchronizationManager.unbindResource(obtainDataSource());
    }

    // 重置只读状态和隔离级别
    try {
        Connection con = txObject.getConnectionHolder().getConnection();
        if (con != null && !con.isClosed()) {
            if (txObject.isMustRestoreAutoCommit()) {
                con.setAutoCommit(true);
            }
            DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
        }
    } catch (Throwable ex) {
        logger.debug("Could not reset JDBC Connection after transaction", ex);
    }

    txObject.getConnectionHolder().clear();
}

总结

DataSourceTransactionManager通过这些核心方法实现了对事务的管理:

  • doBegin:获取数据库连接并配置事务。
  • doCommit:提交事务。
  • doRollback:回滚事务。
  • doCleanupAfterCompletion:在事务完成后清理资源。