MyBatis性能调优:批量操作与延迟加载实战指南

作为Java生态中最受欢迎的ORM框架之一,MyBatis的性能优化是每个开发者都需要掌握的技能。本文将深入探讨批量操作和延迟加载两大核心优化手段,通过原理分析、代码示例和实践建议,帮助你提升数据库访问效率。

一、批量操作优化

批量操作是减少数据库交互次数的有效手段,MyBatis提供了多种实现方式。

1. ExecutorType.BATCH模式

原理分析
MyBatis执行器有三种类型:

  • SIMPLE:默认模式,每条语句单独执行
  • REUSE:重用预处理语句
  • BATCH:批量执行更新操作
// 开启BATCH模式示例
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (int i = 0; i < 1000; i++) {
        mapper.insert(new User("user" + i));
        if (i % 200 == 0) {
            // 分批提交,避免内存溢出
            session.flushStatements();
        }
    }
    session.commit();
} finally {
    session.close();
}

性能对比(基于MySQL测试):

barChart
    title 执行1000次插入操作耗时对比(ms)
    x-axis 模式
    y-axis 耗时
    series "耗时"
        "SIMPLE": 1200
        "REUSE": 900
        "BATCH": 300

实践建议

  1. 批量操作后必须调用commit()rollback(),否则连接不会释放
  2. 大数据量时分批flushStatements(),避免OOM
  3. 查询操作不需要使用BATCH模式

2. 批量插入语法优化

MyBatis中常见的批量插入实现方式:

方式一:foreach动态SQL

<insert id="batchInsert">
    INSERT INTO user(name) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.name})
    </foreach>
</insert>

方式二:多值插入语法(MySQL)

INSERT INTO user(name) VALUES ('user1'),('user2'),('user3');

性能对比

方式10条记录100条记录1000条记录
foreach15ms80ms650ms
多值语法5ms30ms250ms
BATCH模式8ms50ms400ms

最佳实践

  1. 小批量数据(<100条):优先使用多值插入语法
  2. 大批量数据:BATCH模式 + 分批提交
  3. 避免在foreach中拼接超长SQL(某些数据库对SQL长度有限制)

二、延迟加载优化

延迟加载(Lazy Loading)是优化关联查询性能的重要手段。

1. 基础配置

<!-- mybatis-config.xml -->
<settings>
    <!-- 开启延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 设置积极加载策略 -->
    <setting name="aggressiveLazyLoading" value="false"/>
    <!-- 指定代理方式 -->
    <setting name="proxyFactory" value="JAVASSIST"/>
</settings>

2. 代理方式对比

MyBatis支持两种代理实现:

特性Javassist (默认)CGLIB
依赖内置需额外引入cglib
性能较高稍低
代理限制接口代理类代理
初始化速度

选择建议

  • 大多数场景使用默认Javassist即可
  • 需要代理具体类时才使用CGLIB

3. 关联查询优化示例

<!-- 订单与用户的1:1关联 -->
<resultMap id="orderWithUser" type="Order">
    <id property="id" column="id"/>
    <association property="user" column="user_id" 
                 select="com.example.mapper.UserMapper.selectById"
                 fetchType="lazy"/>
</resultMap>

触发加载的场景

Order order = orderMapper.selectById(1);
// 以下操作会触发用户加载
System.out.println(order.getUser().getName()); 

实践建议

  1. 一对多关联优先使用延迟加载
  2. 及时关闭不再需要的SqlSession,避免延迟加载异常
  3. 使用fetchType="lazy"覆盖全局配置
  4. 注意N+1问题:必要时使用join查询

三、综合性能调优策略

  1. 监控先行:使用P6Spy等工具监控SQL执行情况
  2. 批处理:大数据量更新/插入优先考虑BATCH模式
  3. 延迟加载:合理配置关联对象的加载策略
  4. SQL优化:结合执行计划分析慢查询
  5. 连接池:配置合适的连接池参数(如Druid)

图2

通过合理运用这些优化手段,我们曾将一个订单导出功能的性能从最初的120秒优化到15秒,效果显著。记住:没有放之四海而皆准的最优方案,要根据具体业务场景和数据特点选择最适合的优化策略。

添加新评论