Skip to content

事务管理完全指南

字数
1552 字
阅读时间
7 分钟

1 事务的定义

事务(Transaction)是一个操作序列,这些操作要么都执行,要么都不执行,是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在数据库提交工作时,可确保要么所有修改都已保存,要么所有修改都不保存。

2 事务的特性(ACID)

事务具有四个特性,通常称为ACID特性:

2.1 原子性(Atomicity)

  • 事务是不可分割的最小操作单位
  • 事务中的所有操作要么全部完成,要么全部失败回滚
  • 不存在部分完成的情况

2.2 一致性(Consistency)

  • 事务必须使数据库从一个一致性状态变换到另一个一致性状态
  • 保证数据库的完整性约束
  • 例如:转账前后,两个账户的总金额保持不变

2.3 隔离性(Isolation)

  • 多个事务并发执行时,一个事务的执行不应影响其他事务的执行
  • 通过事务隔离级别实现
  • 不同隔离级别提供不同程度的隔离性

2.4 持久性(Durability)

  • 一个事务一旦提交,其对数据库的修改将永久保存
  • 即使系统崩溃,提交的数据也不会丢失

3 并发问题

在多个事务并发执行时,可能会出现以下问题:

3.1 脏读(Dirty Read)

  • 一个事务读取了另一个未提交事务修改过的数据
  • 如果另一个事务回滚,那么当前事务读取的数据就是无效的

3.2 不可重复读(Non-repeatable Read)

  • 一个事务内多次读取同一数据,但其他事务在这期间修改了数据
  • 导致两次读取的结果不一致

3.3 幻读(Phantom Read)

  • 一个事务内多次查询某个范围的记录,但其他事务在这期间插入了新的记录
  • 导致两次查询返回的记录数不一致

4 MySQL事务隔离级别

4.1 READ UNCOMMITTED(读未提交)

  • 最低的隔离级别
  • 允许读取未提交的数据
  • 可能出现脏读、不可重复读和幻读
  • 实际应用中很少使用

4.2 READ COMMITTED(读已提交)

  • 只能读取已经提交的数据
  • 可以避免脏读
  • 可能出现不可重复读和幻读
  • Oracle数据库默认级别

4.3 REPEATABLE READ(可重复读)

  • 保证同一事务中多次读取同样数据的结果一致
  • 可以避免脏读和不可重复读
  • 可能出现幻读
  • MySQL默认的隔离级别

4.4 SERIALIZABLE(串行化)

  • 最高的隔离级别
  • 完全串行化的读写,事务之间完全隔离
  • 可以避免脏读、不可重复读和幻读
  • 性能最差,很少使用

4.5 隔离级别对比

| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|-------- -|------|------------|-- ----|
| READ UNCOMMITTED | 可能 | 可能 | 可能 |
| READ COMMITTED | 不可能 | 可能 | 可能 |
| REPEATABLE READ | 不可能 | 不可能 | 可能 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 |

4.6 MySQL设置隔离级别

sql
-- 查看当前隔离级别
SELECT @@transaction_isolation;

-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

5 需要使用事务管理的场景

  1. 涉及多表操作

    • 多个表数据需要同时更新
    • 保证数据的一致性
  2. 金融交易

    • 转账
    • 支付
    • 退款
  3. 库存管理

    • 下单减库存
    • 订单取消恢复库存
  4. 数据批量处理

    • 批量导入数据
    • 批量更新操作
  5. 关键业务操作

    • 用户注册
    • 订单处理
    • 积分变更

6 Java事务管理实现

6.1 JDBC事务

java
Connection conn = null;
try {
    // 获取连接
    conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
    // 关闭自动提交
    conn.setAutoCommit(false);
    
    // 执行SQL操作
    Statement stmt = conn.createStatement();
    stmt.execute("UPDATE account SET balance = balance - 100 WHERE id = 1");
    stmt.execute("UPDATE account SET balance = balance + 100 WHERE id = 2");
    
    // 提交事务
    conn.commit();
} catch (Exception e) {
    // 发生异常,回滚事务
    if (conn != null) {
        conn.rollback();
    }
    throw e;
} finally {
    // 恢复自动提交
    if (conn != null) {
        conn.setAutoCommit(true);
    }
}

6.2 JTA事务

java
@Resource
UserTransaction utx;

try {
    utx.begin();
    // 执行业务逻辑
    userDAO.updateUser(user);
    orderDAO.createOrder(order);
    utx.commit();
} catch (Exception e) {
    utx.rollback();
    throw e;
}

7 Spring事务管理

7.1 声明式事务

java
@Transactional
public class UserService {
    
    @Transactional(rollbackFor = Exception.class)
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account fromAccount = accountDao.getAccount(fromId);
        Account toAccount = accountDao.getAccount(toId);
        
        fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
        toAccount.setBalance(toAccount.getBalance().add(amount));
        
        accountDao.updateAccount(fromAccount);
        accountDao.updateAccount(toAccount);
    }
}

7.2 编程式事务

java
@Autowired
private TransactionTemplate transactionTemplate;

public void transfer(final Long fromId, final Long toId, final BigDecimal amount) {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            try {
                Account fromAccount = accountDao.getAccount(fromId);
                Account toAccount = accountDao.getAccount(toId);
                
                fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
                toAccount.setBalance(toAccount.getBalance().add(amount));
                
                accountDao.updateAccount(fromAccount);
                accountDao.updateAccount(toAccount);
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        }
    });
}

7.3 Spring事务传播行为

  • REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
  • MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
  • REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
  • NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
  • NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行

7.4 Spring事务配置示例

java
@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Bean
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
        return new TransactionTemplate(transactionManager);
    }
}
`
``