Java慢SQL拦截方案详解
基于MyBatis拦截器的实现
@Component
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SlowSqlInterceptor implements Interceptor {
private static final Logger logger = LoggerFactory.getLogger(SlowSqlInterceptor.class);
// 慢SQL阈值(毫秒)
private static final long SLOW_SQL_THRESHOLD = 1000;
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return invocation.proceed();
} finally {
long endTime = System.currentTimeMillis();
long costTime = endTime - startTime;
if (costTime > SLOW_SQL_THRESHOLD) {
// 获取SQL信息
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
String sql = boundSql.getSql();
String sqlId = mappedStatement.getId();
// 记录慢SQL
logger.warn("Slow SQL detected - SQL ID: {}, Cost: {}ms, SQL: {}",
sqlId, costTime, formatSql(sql));
// 发送告警通知(可选)
sendAlert(sqlId, costTime, sql);
}
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可以读取配置参数
}
// 格式化SQL
private String formatSql(String sql) {
return sql.replaceAll("\\s+", " ").trim();
}
// 发送告警(可扩展为钉钉、邮件等)
private void sendAlert(String sqlId, long costTime, String sql) {
// 异步发送告警
CompletableFuture.runAsync(() -> {
// 实现告警逻辑
logger.info("Sending alert for slow SQL: {}", sqlId);
});
}
}
基于Spring AOP的实现
@Aspect
@Component
public class SlowQueryAspect {
private static final Logger logger = LoggerFactory.getLogger(SlowQueryAspect.class);
@Autowired
private SlowSqlMonitor slowSqlMonitor;
// 拦截Mapper接口
@Around("execution(* com.example.mapper.*.*(..))")
public Object aroundMapperMethods(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long costTime = System.currentTimeMillis() - startTime;
if (costTime > slowSqlMonitor.getThreshold()) {
String methodName = pjp.getSignature().getDeclaringTypeName()
+ "." + pjp.getSignature().getName();
// 记录慢查询
slowSqlMonitor.record(methodName, costTime, pjp.getArgs());
}
}
}
}
完整监控实现(包括告警和统计)
@Service
public class SlowSqlMonitorService {
private static final Logger logger = LoggerFactory.getLogger(SlowSqlMonitorService.class);
// 慢SQL配置
@Value("${slow.sql.threshold:1000}")
private long slowSqlThreshold;
@Value("${slow.sql.alert.enabled:true}")
private boolean alertEnabled;
// 慢SQL统计缓存
private final Cache<String, SlowSqlStat> statsCache = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(1, TimeUnit.HOURS)
.build();
@EventListener
public void handleSlowSqlEvent(SlowSqlEvent event) {
// 1. 记录日志
logSlowSql(event);
// 2. 更新统计
updateStats(event);
// 3. 发送告警
if (alertEnabled && event.getCostTime() > slowSqlThreshold * 10) {
sendAlertForVerySlowQuery(event);
}
// 4. 保存到数据库(可选)
saveSlowSqlRecord(event);
}
private void logSlowSql(SlowSqlEvent event) {
logger.warn("Slow SQL Alert - SQL ID: {}, Cost: {}ms, SQL: {}, Params: {}",
event.getSqlId(),
event.getCostTime(),
event.getSql(),
JSON.toJSONString(event.getParams())
);
}
private void updateStats(SlowSqlEvent event) {
String key = event.getSqlId();
try {
statsCache.get(key, () -> {
SlowSqlStat stat = new SlowSqlStat();
stat.setSqlId(key);
return stat;
});
// 更新统计信息
SlowSqlStat stat = statsCache.getIfPresent(key);
if (stat != null) {
stat.incrementCount();
stat.addTotalTime(event.getCostTime());
stat.setMaxTime(Math.max(stat.getMaxTime(), event.getCostTime()));
stat.setSql(event.getSql());
// 如果超过告警阈值,执行告警
if (stat.getCount() % 10 == 0) {
statsCache.put(key, stat);
logger.warn("Slow SQL frequency alert - {}: {} times, avg {}ms",
key, stat.getCount(), stat.getAvgTime());
}
}
} catch (Exception e) {
logger.error("Failed to update stats", e);
}
}
private void sendAlertForVerySlowQuery(SlowSqlEvent event) {
// 集成钉钉/企业微信/邮件告警
String content = String.format(
"慢SQL告警\nSQL ID: %s\n执行时间: %dms\nSQL: %s",
event.getSqlId(),
event.getCostTime(),
event.getSql()
);
// 异步发送告警
CompletableFuture.runAsync(() -> {
try {
// 钉钉告警示例
DingTalkClient.sendAlert(content);
// 邮件告警
mailService.sendAlertEmail(content);
} catch (Exception e) {
logger.error("Failed to send alert", e);
}
});
}
private void saveSlowSqlRecord(SlowSqlEvent event) {
// 保存到数据库
try {
sqlLogMapper.insert(event.toEntity());
} catch (Exception e) {
logger.error("Failed to save slow sql record", e);
}
}
// 慢SQL统计类
@Data
public static class SlowSqlStat {
private String sqlId;
private AtomicLong count = new AtomicLong(0);
private AtomicLong totalTime = new AtomicLong(0);
private volatile long maxTime;
private String sql;
public void incrementCount() {
count.incrementAndGet();
}
public void addTotalTime(long time) {
totalTime.addAndGet(time);
}
public long getAvgTime() {
long c = count.get();
return c == 0 ? 0 : totalTime.get() / c;
}
}
}
// 慢SQL事件类
@Data
public class SlowSqlEvent {
private String sqlId;
private String sql;
private Object params;
private long costTime;
private Date timestamp;
private String threadName;
}
基于JDBC Connection拦截
@Configuration
public class JdbcInterceptorConfig {
@Bean
public HikariCPConnectionInterceptor hikariCPConnectionInterceptor() {
return new HikariCPConnectionInterceptor();
}
public static class HikariCPConnectionInterceptor {
private static final Logger logger = LoggerFactory.getLogger(HikariCPConnectionInterceptor.class);
@Bean
public ProxyFactoryBean proxyFactoryBean(DataSource dataSource) {
ProxyFactoryBean pfb = new ProxyFactoryBean();
pfb.setTarget(dataSource);
pfb.setInterceptorNames("jdbcInterceptor");
return pfb;
}
@Bean
public JdbcInterceptor jdbcInterceptor() {
return new JdbcInterceptor() {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
String methodName = methodInvocation.getMethod().getName();
if ("prepareStatement".equals(methodName)) {
Object[] args = methodInvocation.getArguments();
String sql = (String) args[0];
// 对SQL进行包装
args[0] = new SlowSqlPreparedStatement(sql);
methodInvocation.getArguments()[0] = args[0];
}
return methodInvocation.proceed();
}
};
}
}
// 自定义PreparedStatement包装器
public static class SlowSqlPreparedStatement implements InvocationHandler {
private final String sql;
private final long startTime;
public SlowSqlPreparedStatement(String sql) {
this.sql = sql;
this.startTime = System.currentTimeMillis();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("executeQuery".equals(method.getName()) || "executeUpdate".equals(method.getName())) {
long elapsed = System.currentTimeMillis() - startTime;
if (elapsed > 1000) {
logger.warn("Slow SQL: {} - {}ms", sql, elapsed);
}
}
return method.invoke(proxy, args);
}
}
}
使用配置和扩展点
# application.yml配置
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
connection-timeout: 30000
validation-timeout: 5000
idle-timeout: 600000
max-lifetime: 1800000
maximum-pool-size: 20
minimum-idle: 5
# 自定义慢SQL配置
slow-sql:
enabled: true
threshold: 1000 # 毫秒
alert-enabled: true
alert-frequency: 10 # 每10次同SQL告警
sql-log:
enabled: true
max-length: 2000
save-to-db: true
// 使用示例
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private SlowSqlMonitorService slowSqlMonitor;
public User getUserWithComplexQuery(Long id) {
long startTime = System.currentTimeMillis();
try {
// 可能产生慢查询的业务逻辑
List<User> users = userMapper.findComplexUsers(id);
return users.stream().findFirst().orElse(null);
} finally {
long costTime = System.currentTimeMillis() - startTime;
if (costTime > 1000) {
// 手动记录慢查询
slowSqlMonitor.recordSlowSql(
"UserService.getUserWithComplexQuery",
costTime,
"SELECT ... FROM user WHERE ..."
);
}
}
}
}
监控效果
// 使用示例 - 输出效果 // [WARN] Slow SQL detected - SQL ID: com.example.mapper.UserMapper.selectUsers, // Cost: 1500ms, SQL: SELECT * FROM users WHERE status = 1 ORDER BY create_time DESC // // [INFO] Slow SQL stats - UserMapper.selectUsers: 5 times, avg 1200ms, max 2000ms
通过以上实现,可以有效监控Java应用中的慢SQL问题,及时发现问题并进行优化。

版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播