Java事务性能优化实战指南

一、批量操作的事务批处理

JDBC批处理API

JDBC批处理是提升数据库操作性能的基础手段,通过减少网络往返次数显著提高吞吐量。

// JDBC批处理基础示例
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false); // 关闭自动提交

try (PreparedStatement ps = conn.prepareStatement("INSERT INTO orders VALUES (?, ?)")) {
    for (int i = 0; i < 1000; i++) {
        ps.setInt(1, i);
        ps.setString(2, "order_" + i);
        ps.addBatch();  // 添加到批处理
        
        if (i % 100 == 0) { // 每100条执行一次
            ps.executeBatch();
            conn.commit(); // 分批提交
        }
    }
    ps.executeBatch(); // 执行剩余批次
    conn.commit();
} catch (SQLException e) {
    conn.rollback(); // 异常回滚
}

关键参数调优

  • rewriteBatchedStatements=true (MySQL驱动参数,提升批处理性能)
  • batchSize 根据内存和网络情况设置合理值(通常500-1000)

MyBatis批量插入优化

MyBatis提供两种批处理方式,各有适用场景:

<!-- 方式1:foreach动态SQL -->
<insert id="batchInsert">
    INSERT INTO users (name, email) VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.name}, #{user.email})
    </foreach>
</insert>

<!-- 方式2:ExecutorType.BATCH -->
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (User user : users) {
        mapper.insert(user);
    }
    session.commit(); // 统一提交
} finally {
    session.close();
}

性能对比测试(插入1万条数据):

方式耗时(ms)内存消耗
单条循环插入4200
foreach动态SQL850
ExecutorType.BATCH650

实践建议

  1. 小批量数据(100条内)用foreach更简单
  2. 大批量数据用BATCH模式更高效
  3. 超大批量(10万+)考虑文件导入等替代方案

二、连接池配置与事务关系

HikariCP事务隔离配置

连接池配置直接影响事务性能,HikariCP作为高性能连接池的代表:

# Spring Boot配置示例
spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      isolation-level: TRANSACTION_READ_COMMITTED
      leak-detection-threshold: 5000 # 连接泄漏检测阈值(ms)
      connection-timeout: 3000

隔离级别与性能关系

图1

关键配置原则

  1. 连接数 = (核心数 * 2) + 有效磁盘数
  2. 事务隔离级别根据业务需求选择,通常READ_COMMITTED是平衡点
  3. 监控连接等待时间,超过100ms应考虑扩容

连接泄漏检测

连接泄漏是长事务的常见副作用,可通过以下方式检测:

// 编程式泄漏检测
HikariDataSource ds = (HikariDataSource)dataSource;
ds.getHikariPoolMXBean().getActiveConnections();
ds.getHikariPoolMXBean().getThreadsAwaitingConnection();

// 日志分析模式
logging.level.com.zaxxer.hikari=DEBUG

泄漏处理流程

  1. 通过SHOW FULL PROCESSLIST定位泄漏连接
  2. 分析线程栈找到持有连接的代码位置
  3. 修复后通过KILL <process_id>释放连接

三、长事务拆分策略

异步化处理

将长事务拆分为多个异步阶段是常见优化手段:

// 基于Spring @Async的异步化改造
@Service
public class OrderService {
    
    @Transactional
    public void processOrder(Order order) {
        // 1. 同步处理核心业务
        orderDao.save(order);
        inventoryService.reduceStock(order);
        
        // 2. 异步处理非核心逻辑
        asyncService.logOperation(order);
        asyncService.sendNotification(order);
    }
    
    @Async
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOperation(Order order) {
        // 独立事务处理日志
    }
}

异步事务注意事项

  1. 异步方法需在不同类中调用(Spring AOP限制)
  2. 配置@EnableAsync和线程池:

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("AsyncTx-");
        executor.initialize();
        return executor;
    }

状态机分片

复杂业务流程适合用状态机拆分:

图2

状态机实现方案

  1. Spring StateMachine
  2. 自研状态模式实现
  3. 数据库状态字段+版本号控制

实践建议

  1. 每个状态变更对应独立事务
  2. 状态变更日志记录完整操作历史
  3. 补偿机制处理失败状态

性能优化检查清单

  1. 批处理检查

    • [ ] 是否合理设置batchSize(500-1000)
    • [ ] MySQL开启rewriteBatchedStatements
    • [ ] 避免批处理中途发生OOM
  2. 连接池检查

    • [ ] 连接数配置符合(核心数*2 + 磁盘数)公式
    • [ ] 启用连接泄漏检测
    • [ ] 监控连接等待时间
  3. 长事务检查

    • [ ] 单个事务执行时间不超过1秒
    • [ ] 事务内RPC调用不超过1次
    • [ ] 事务内没有文件IO操作

通过以上优化手段,典型电商系统的订单处理TPS可从200提升到1500+,同时系统稳定性得到显著提高。实际应用中应根据具体业务特点进行参数调优和方案组合。

添加新评论