
本文介绍如何通过 mybatis 插件机制实现轻量级、无侵入的全局 sql 执行性能监听,自动捕获超时查询与高负载操作,并支持毫秒级响应时间统计和结果集规模预警。
本文介绍如何通过 mybatis 插件机制实现轻量级、无侵入的全局 sql 执行性能监听,自动捕获超时查询与高负载操作,并支持毫秒级响应时间统计和结果集规模预警。
在企业级 Java 应用中,数据库性能往往是系统瓶颈的关键来源。MyBatis 作为主流持久层框架,其 SQL 执行效率直接影响整体 QoS(服务质量)。为实现“零改造业务代码”的性能可观测性,推荐采用 MyBatis Interceptor(插件)机制——它基于 JDK 动态代理,在 Executor 层统一拦截所有 query 和 update 操作,无需修改 Mapper 接口或 Service 层逻辑。
以下是一个生产就绪的性能监听拦截器示例,不仅统计执行耗时,还可扩展支持结果集大小预警:
import java.text.MessageFormat;
import java.util.List;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
@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 PerformanceMonitoringInterceptor implements Interceptor {
private static final Log log = LogFactory.getLog(PerformanceMonitoringInterceptor.class);
// 可配置阈值(单位:纳秒),建议默认设为 100_000_000L ≈ 100ms
private long thresholdNanos = 100_000_000L;
// 结果集行数预警阈值(仅对 query 生效)
private int resultSizeThreshold = 1000;
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.nanoTime();
Object result = invocation.proceed();
long duration = System.nanoTime() - start;
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
String statementId = ms.getId();
// 耗时告警(DEBUG 级别日志,避免线上性能损耗)
if (log.isDebugEnabled() && duration > thresholdNanos) {
log.debug(MessageFormat.format(
"[PERF-WARN] Statement '{0}' took {1, number,###} ms (threshold: {2, number,###} ms)",
statementId, duration / 1_000_000.0, thresholdNanos / 1_000_000.0));
}
// 结果集规模预警(仅对查询且返回 List 的场景)
if (result instanceof List> list && list.size() > resultSizeThreshold) {
log.warn(MessageFormat.format(
"[DATA-WARN] Statement '{0}' returned {1} rows (threshold: {2})",
statementId, list.size(), resultSizeThreshold));
}
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
String threshold = properties.getProperty("thresholdMillis", "100");
this.thresholdNanos = Long.parseLong(threshold) * 1_000_000L;
String sizeThreshold = properties.getProperty("resultSizeThreshold", "1000");
this.resultSizeThreshold = Integer.parseInt(sizeThreshold);
}
}
✅ 关键特性说明:
- ✅ 双维度监控:同时支持「执行耗时」与「结果集规模」双阈值预警;
- ✅ 零侵入集成:不依赖 Spring AOP 或字节码增强,完全兼容原生 MyBatis 与 mybatis-spring-boot-starter;
- ✅ 可配置化:通过 setProperties() 支持 XML 或 @ConfigurationProperties 动态注入阈值;
- ✅ 日志分级控制:耗时日志使用 DEBUG 级别,线上可关闭;严重超限/大数据量使用 WARN,确保可追溯。
? 注册方式(任选其一):
-
XML 配置(mybatis-config.xml):
<plugins><plugin interceptor="com.example.PerformanceMonitoringInterceptor"><property name="thresholdMillis" value="50"></property><property name="resultSizeThreshold" value="500"></property></plugin></plugins>
-
Spring Boot 自动装配(推荐):
@Configuration public class MyBatisConfig { @Bean public Interceptor performanceMonitor() { PerformanceMonitoringInterceptor interceptor = new PerformanceMonitoringInterceptor(); Properties props = new Properties(); props.setProperty("thresholdMillis", "50"); props.setProperty("resultSizeThreshold", "500"); interceptor.setProperties(props); return interceptor; } }
⚠️ 注意事项:
- 避免在 intercept() 中执行耗时操作(如远程调用、复杂计算),否则会污染原始性能指标;
- System.nanoTime() 是最精准的计时方式,优于 System.currentTimeMillis();
- 若需采集更丰富指标(如 SQL 参数、慢 SQL 样本、调用堆栈),建议结合 Micrometer + Prometheus 或接入 SkyWalking 等 APM 工具;
- 生产环境务必关闭 DEBUG 日志,或通过日志门控(如 Logback 的 ThresholdFilter)限制性能日志输出频率。
通过该拦截器,你可在 5 分钟内为整个 MyBatis 应用装上“性能雷达”,快速识别慢 SQL、N+1 查询及数据膨胀风险,真正实现质量左移与主动式运维。










