mybatis统计sql耗时最推荐拦截executor的doquery/doupdate/dobatch方法,因其覆盖完整执行流程;需实现interceptor接口,用system.nanotime()精准计时,提取mappedstatement获取sql信息,并配置开关控制与阈值告警、聚合统计及监控对接。

MyBatis 通过 Interceptor 接口可拦截 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler 四类对象的执行过程。要统计 SQL 执行耗时,最直接有效的方式是拦截 StatementHandler 的 prepare() 方法(获取 Statement)和 query()/update() 方法(实际执行),或更推荐拦截 Executor 的 doQuery()/doUpdate()/doBatch() 等方法——因为它们包裹了完整的 SQL 执行流程(含参数设置、语句准备、执行、结果处理),且天然支持所有操作类型。
1. 编写耗时监控拦截器
实现 org.apache.ibatis.plugin.Interceptor,在 intercept() 中记录开始时间、调用目标方法、计算耗时并打印/上报:
- 使用
System.nanoTime()(纳秒级,精度高,不受系统时间调整影响) - 通过
invocation.getTarget()获取被拦截对象(如 Executor),用invocation.getMethod()判断是否为doQuery等关键方法 - 从
invocation.getArgs()中提取MappedStatement,可获取 SQL ID(ms.getId())、SQL 语句(ms.getBoundSql().getSql())等上下文信息 - 建议增加开关控制(如配置项
mybatis.monitor.enabled=true),避免线上全量打日志
2. 配置拦截器生效
在 MyBatis 配置文件(如 mybatis-config.xml)中注册拦截器:
<plugins><plugin interceptor="com.example.MyBatisMonitorInterceptor"><property name="enabled" value="true"></property></plugin></plugins>
若使用 Spring Boot,也可通过 @Bean 方式注入 ConfigurationCustomizer 或直接向 SqlSessionFactoryBean 的 plugins 属性添加实例。
3. 增强实用性:区分慢 SQL 与聚合统计
单纯打印每条 SQL 耗时不具运维价值,建议升级为:
-
阈值告警:例如耗时 > 500ms 记录 WARN 日志,并输出完整 SQL、参数、执行堆栈(可用
Thread.currentThread().getStackTrace()) -
轻量聚合:用
ConcurrentHashMap按 SQL ID 统计平均耗时、最大耗时、QPS、错误率(捕获SQLException),定期(如每分钟)输出 Top N 慢 SQL -
对接监控系统:将指标导出为 Micrometer 的
Timer或Counter,接入 Prometheus + Grafana;或通过 Logback 的AsyncAppender异步推送至 ELK/Splunk
4. 注意事项与避坑点
拦截器运行在 MyBatis 核心链路中,需保证轻量、线程安全、无副作用:
- 避免在拦截逻辑中做 I/O(如写文件、远程调用),否则会显著拖慢 SQL 执行
- 不要修改
invocation.getArgs()中的参数对象(除非明确需要),尤其不要替换BoundSql或ParameterMapping,易引发不可预知行为 - 注意事务上下文:Executor 拦截器在同一个事务中可能被复用,但每个 SQL 执行都是独立
invocation,无需手动管理生命周期 - 若使用分页插件(如 PageHelper),确保你的拦截器
@Intercepts的签名不与其冲突(如都拦截Executor的同名方法),可通过signature.type()和signature.method()显式限定
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











