后端必看!Spring Boot3 中记录 MyBatis SQL 执行时间的高效方法

后端必看!Spring Boot3 中记录 MyBatis SQL 执行时间的高效方法

精选文章moguli202025-06-04 22:25:214A+A-

你有没有遇到过这样的困扰?在使用 Spring Boot3 和 MyBatis 开发后端项目时,项目上线后,数据库操作越来越频繁,性能问题逐渐暴露。想要排查问题,却不知道从哪里入手?尤其是 SQL 执行效率低下,严重影响了系统的响应速度,却连具体哪条 SQL 耗时较长都不清楚,这可怎么办?

在如今互联网应用规模不断扩大、业务复杂度日益提升的背景下,对于互联网大厂的后端开发人员来说,Spring Boot3 和 MyBatis 是常用的开发框架组合。系统的性能优化成为了开发过程中的关键环节,而准确记录 SQL 执行时间,是定位和解决性能问题的重要前提。只有知道了每条 SQL 语句的执行时长,才能有针对性地进行优化,提升整个系统的运行效率,给用户带来更好的使用体验。如果不能及时发现并解决 SQL 执行效率问题,可能会导致用户流失,影响公司业务发展。

使用 AOP(面向切面编程)

AOP 是 Spring 框架的核心技术之一,它允许开发者在不修改源代码的情况下,增加额外的功能。我们可以通过定义一个切面(Aspect),在方法执行前后插入自定义的逻辑,来记录 SQL 执行时间。首先,在项目的 pom.xml 文件中引入 AOP 相关的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

然后,定义一个切面类,例如SqlExecutionTimeAspect:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class SqlExecutionTimeAspect {
    private static final Logger logger = LoggerFactory.getLogger(SqlExecutionTimeAspect.class);

    @Around("execution(* com.example.dao.*.*(..))") // 这里根据实际的DAO包路径进行调整
    public Object recordSqlExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        logger.info("SQL执行时间:{}ms", executionTime);
        return result;
    }
}

在上述代码中,@Around注解定义了一个环绕通知,execution(* com.example.dao.*.*(..))表示匹配指定包下 DAO 接口的所有方法。在方法执行前后获取当前时间,计算时间差并记录到日志中。

使用 MyBatis 拦截器

MyBatis 提供了插件机制,我们可以通过实现Interceptor接口来拦截 SQL 执行过程,并添加自定义逻辑。新建一个 MyBatis 的拦截器类,例如SqlTimeInterceptor:

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Properties;

@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class SqlTimeInterceptor implements Interceptor {
    private static final Logger logger = LoggerFactory.getLogger(SqlTimeInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = invocation.proceed();
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;

        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        BoundSql boundSql = ms.getBoundSql(args[1]);
        String sql = boundSql.getSql();

        logger.info("SQL: {},执行时间:{}ms", sql, executionTime);

        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

接着,需要将拦截器注册到 MyBatis 的配置中。在 Spring Boot3 的配置类中,添加如下代码:

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setPlugins(new SqlTimeInterceptor()); // 注册拦截器
        return factoryBean.getObject();
    }
}

这样,每次执行 SQL 时,都会记录 SQL 语句及其执行时间。

使用数据源代理(如 datasource - proxy)

datasource - proxy是一个强大的数据源代理工具,它可以在不修改代码的情况下,记录 SQL 执行时间等信息。首先,在 pom.xml 文件中添加依赖:

<dependency>
    <groupId>com.github.ttddyy</groupId>
    <artifactId>datasource-proxy</artifactId>
    <version>1.10</version>
</dependency>

然后,在 application.properties 或 application.yml 中配置数据源代理的相关属性,例如在 application.yml 中:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/your_database
    username: your_username
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      connection-timeout: 30000
      maximum-pool-size: 10
    proxy:
      log:
        level: DEBUG # 设置日志级别
        slowQueryThreshold: 500 # 设置慢查询阈值为500ms

启动应用后,所有通过 JDBC 执行的 SQL 语句及其执行耗时都将记录在日志中。

使用 Druid 连接池

Druid 是一个功能强大的数据库连接池,它自带的监控工具 Druid Monitor 可以方便地查看 SQL 执行时间等监控信息。在 pom.xml 中引入 Druid 的依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.16</version>
</dependency>

在 application.yml 中进行相关配置:

spring:
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/your_database
      username: your_username
      password: your_password
      driver-class-name: com.mysql.cj.jdbc.Driver
      initial-size: 5
      min-idle: 5
      maxActive: 20
      maxWait: 60000
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      filters: stat,slf4j # 启用统计和日志过滤
      connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=500

配置完成后,通过访问 Druid Monitor 的地址(默认是/druid,具体可根据配置调整),输入用户名和密码(默认都是 admin,也可在配置中修改),就可以在监控页面查看 SQL 执行时间、慢 SQL 等详细信息。

使用 p6spy

p6spy是一个强大的 SQL 监控工具,它可以拦截 JDBC 调用,记录 SQL 执行时间等信息。在 pom.xml 中引入p6spy的依赖:

<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.9.1</version>
</dependency>

在 resource 目录下新建spy.properties文件,进行相关配置:

# 模块配置
modulelist=com.p6spy.engine.spy.P6SpyFactory,com.p6spy.engine.logging.appender.StdoutLogger

# 日志格式
logMessageFormat=com.p6spy.engine.spy.appender.MessageFormattingStrategyImpl

# 开启慢SQL记录,阈值为500ms
appender=com.p6spy.engine.logging.appender.FileLogger
# 日志文件路径
logfile=spy.log
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 真实JDBC驱动
driverlist=com.mysql.cj.jdbc.Driver

启动项目后,p6spy就会对 SQL 执行时间进行记录并输出到spy.log文件中。

通过以上几种方法,我们就可以在 Spring Boot3 项目中方便地记录 MyBatis 执行 SQL 的时间。无论是使用 AOP、MyBatis 拦截器,还是数据源代理、Druid 连接池、p6spy,都各有优势。大家可以根据项目的实际需求和特点,选择最适合的方式。

如果你在实际操作过程中,使用了这些方法,或者还有其他更好的记录 SQL 执行时间的技巧,欢迎在评论区分享交流!也别忘了点赞、收藏这篇文章,方便后续回顾和学习,一起提升后端开发技能,打造更高效的系统!

点击这里复制本文地址 以上内容由莫古技术网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!
qrcode

莫古技术网 © All Rights Reserved.  滇ICP备2024046894号-2