在现代企业级应用开发中,MyBatis作为主流的持久层框架,其性能直接影响系统响应速度。本文结合最新实践案例,总结MyBatis及MyBatis-Plus的优化策略,帮助开发者规避常见性能陷阱。


一、SQL语句优化:从源头减少损耗

  1. 精准查询字段
    避免使用SELECT *,仅查询业务所需字段。通过<sql>标签定义可复用字段集合,结合<include>引用,减少重复书写。例如:

    <sql id="base_fields">id, name, status, create_time</sql>
    <select id="selectUsers" resultType="User">
      SELECT <include refid="base_fields"/> FROM user WHERE status = #{status}
    </select>
    
  2. 动态条件优化
    • 使用<if>标签时,优先通过Service层预处理参数,减少动态SQL复杂度。
    • 避免在IN子句中传递超过1000个参数,建议分批处理或改用临时表。
    • 用DECODECASE WHEN替代复杂<choose>逻辑,减少SQL膨胀和硬解析。

  3. 批量操作合并
    使用<foreach>生成批量插入/更新语句,减少数据库交互次数。例如:

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

二、缓存策略:平衡效率与一致性

  1. 一级缓存(SqlSession级)
    默认开启,需配合@Transactional注解生效。适用于单事务内重复查询场景。

  2. 二级缓存(Mapper级)
    需配置mybatis.configuration.cache-enabled=true并添加@CacheNamespace注解。注意:不推荐跨Mapper共享热点数据,易引发缓存雪崩。

  3. 三级缓存(分布式场景)
    在二级缓存基础上集成Redis,通过@Cacheable注解实现跨服务缓存一致性。


三、分页性能优化:避免深分页陷阱

  1. 插件选择与配置
    使用MyBatis-Plus的PaginationInterceptor或PageHelper,生成带LIMIT的SQL。建议设置count=false跳过统计总记录数。

  2. 自定义COUNT查询
    对复杂查询重写COUNT逻辑,仅统计主表数据。例如:

    <!-- 原分页查询 -->
    <select id="selectByCondition" resultMap="BaseResultMap">
      SELECT a.*, b.field FROM table_a a LEFT JOIN table_b b ON a.id = b.aid WHERE a.status = #{status}
    </select>
    <!-- 自定义COUNT查询 -->
    <select id="selectByCondition_COUNT" resultType="Long">
      SELECT COUNT(DISTINCT a.id) FROM table_a a WHERE a.status = #{status}
    </select>
    

四、框架配置优化:连接池与监控

  1. 连接池参数调优
    推荐使用HikariCP或Druid,配置核心参数:

    spring.datasource.hikari.maximum-pool-size=20
    spring.datasource.hikari.minimum-idle=5
    
  2. 集成监控工具
    通过Druid监控SQL执行时间,识别慢查询。配置示例:

    @Bean
    public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
      return new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
    }
    

五、拦截器与插件优化:警惕长SQL拼接

典型案例:某项目因MyBatis-Plus租户拦截器拼接超长SQL(含上千个IN参数),导致单次查询耗时从几十ms飙升至1秒。
解决方案

  1. 使用@InterceptorIgnore注解忽略非必要拦截器。
  2. 自行拼接动态条件,避免插件处理长SQL。
  3. 对长列表分批查询,减少单次占位符数量。

其他注意事项

  1. 注释处理
    避免在SQL中使用--注释,改用XML注释<!-- -->,防止分页插件解析错误。

  2. 参数类型匹配
    确保Java实体类字段类型与数据库严格对应(如java.util.Date对应Oracle DATE),避免隐式转换开销。

  3. 日志级别控制
    生产环境关闭MyBatis详细日志(log4j.logger.org.mybatis=ERROR),减少IO开销。


总结

MyBatis性能优化需从SQL编写、缓存策略、框架配置多维度入手。对于复杂场景,建议结合EXPLAIN PLAN分析执行计划,并通过Arthas等工具定位性能瓶颈。通过上述优化,可显著提升系统吞吐量,降低响应延迟。