最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis SQL耗時監(jiān)控的最佳實踐

 更新時間:2025年08月06日 09:01:10   作者:天天摸魚的java工程師  
在性能優(yōu)化的道路上,SQL執(zhí)行時間往往是關鍵突破口,本文將從一個八年經(jīng)驗Java開發(fā)者的視角,帶你深入探索MyBatis SQL耗時監(jiān)控的最佳實踐,需要的朋友可以參考下

一、為什么我們需要關注SQL執(zhí)行時間?

在我多年的開發(fā)經(jīng)歷中,數(shù)據(jù)庫性能瓶頸往往是系統(tǒng)優(yōu)化的關鍵點。特別是在微服務架構和高并發(fā)場景下,一個未優(yōu)化的SQL可能拖垮整個系統(tǒng)。以下是幾個典型的業(yè)務場景:

  1. 生產(chǎn)環(huán)境性能問題排查:用戶突然反饋系統(tǒng)變慢,需要快速定位慢SQL
  2. 接口性能優(yōu)化:API響應時間超過預期,需要分析數(shù)據(jù)庫操作耗時
  3. 慢SQL監(jiān)控:建立系統(tǒng)化的慢查詢監(jiān)控機制
  4. SQL優(yōu)化驗證:驗證SQL優(yōu)化措施的實際效果

二、技術方案選型:為什么選擇MyBatis攔截器?

在Java生態(tài)中,我們有多種方式可以實現(xiàn)SQL耗時統(tǒng)計:

方案優(yōu)點缺點
MyBatis攔截器原生支持、侵入性低、精準控制需要理解MyBatis內(nèi)部機制
AOP切面通用性強、與ORM解耦無法獲取SQL語句細節(jié)
日志框架簡單直接日志量大、難以結構化處理
數(shù)據(jù)庫監(jiān)控無需代碼改動脫離應用上下文、配置復雜

作為有經(jīng)驗的開發(fā)者,我推薦使用MyBatis攔截器方案。它提供了最直接的SQL執(zhí)行切入點,能獲取最豐富的執(zhí)行上下文信息,且對業(yè)務代碼零侵入。

三、核心實現(xiàn):從攔截器到耗時統(tǒng)計

3.1 實現(xiàn)原理

MyBatis攔截器基于責任鏈模式,我們可以通過實現(xiàn)Interceptor接口,在以下關鍵點插入邏輯:

  1. Executor.update() - 攔截增刪改操作
  2. Executor.query() - 攔截查詢操作

3.2 完整代碼實現(xiàn)

import org.apache.ibatis.cache.CacheKey;
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 = "update", 
               args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query",
               args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "query",
               args = {MappedStatement.class, Object.class, RowBounds.class, 
                       ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class SqlCostInterceptor implements Interceptor {
    
    private static final Logger logger = LoggerFactory.getLogger("SQL_PERF");
    
    // 慢查詢閾值(毫秒)
    private long slowQueryThreshold = 1000;
    
    // 是否打印參數(shù)
    private boolean showParameters = true;
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];
        Object parameterObject = args[1];
        
        // 獲取SQL信息
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
        String sqlId = mappedStatement.getId();
        String sql = boundSql.getSql().replaceAll("\\s+", " ");
        
        long start = System.currentTimeMillis();
        try {
            return invocation.proceed(); // 執(zhí)行目標方法
        } finally {
            long cost = System.currentTimeMillis() - start;
            logSqlPerformance(sqlId, sql, parameterObject, cost);
        }
    }
    
    private void logSqlPerformance(String sqlId, String sql, 
                                   Object params, long cost) {
        // 基礎日志
        String logMsg = String.format("SQL執(zhí)行耗時: %dms | ID: %s | SQL: %s",
                                     cost, sqlId, sql);
        
        // 參數(shù)日志(敏感數(shù)據(jù)需脫敏)
        if (showParameters && params != null) {
            String paramStr = params.toString();
            // 簡單脫敏處理(實際項目應使用專業(yè)脫敏工具)
            paramStr = paramStr.replaceAll("(\"password\":\")([^\"]+)(\")", 
                                          "$1****$3");
            logMsg += " | 參數(shù): " + paramStr;
        }
        
        // 分級日志輸出
        if (cost > slowQueryThreshold) {
            logger.warn("[慢查詢告警] {}", logMsg);
        } else if (cost > 500) {
            logger.info("{}", logMsg);
        } else {
            logger.debug("{}", logMsg);
        }
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {
        // 從配置加載參數(shù)
        String threshold = properties.getProperty("slowQueryThreshold");
        if (threshold != null) {
            this.slowQueryThreshold = Long.parseLong(threshold);
        }
        
        String showParams = properties.getProperty("showParameters");
        if (showParams != null) {
            this.showParameters = Boolean.parseBoolean(showParams);
        }
    }
}

3.3 關鍵代碼解析

  1. @Intercepts注解:定義攔截的類和方法簽名
  2. BoundSql獲取:通過MappedStatement獲取實際執(zhí)行的SQL
  3. 參數(shù)脫敏處理:防止敏感數(shù)據(jù)(如密碼)泄露到日志
  4. 分級日志輸出:根據(jù)耗時長短使用不同日志級別
  5. 可配置化:通過setProperties方法實現(xiàn)閾值可配置

3.4 Spring Boot集成配置

@Configuration
public class MyBatisConfig {
    
    @Bean
    public SqlCostInterceptor sqlCostInterceptor() {
        SqlCostInterceptor interceptor = new SqlCostInterceptor();
        Properties properties = new Properties();
        properties.setProperty("slowQueryThreshold", "500"); // 500ms以上視為慢查詢
        properties.setProperty("showParameters", "true");
        interceptor.setProperties(properties);
        return interceptor;
    }
}

四、生產(chǎn)環(huán)境進階技巧

4.1 性能優(yōu)化策略

異步日志輸出:避免日志IO阻塞業(yè)務線程

private final ExecutorService logExecutor = 
    Executors.newSingleThreadExecutor();

private void logSqlPerformance(...) {
    logExecutor.submit(() -> {
        // 日志記錄邏輯
    });
}

采樣率控制:高并發(fā)場景下按比例采樣

// 在intercept方法中加入
if (ThreadLocalRandom.current().nextDouble() < 0.2) {
    // 只記錄20%的請求
}

4.2 監(jiān)控系統(tǒng)集成

將SQL耗時數(shù)據(jù)發(fā)送到監(jiān)控系統(tǒng)(如Prometheus):

private void recordMetrics(String sqlId, long cost) {
    // Prometheus指標記錄
    Summary.builder("sql_execution_time")
           .tag("sql_id", sqlId)
           .register()
           .observe(cost);
    
    // 慢查詢計數(shù)器
    if (cost > slowQueryThreshold) {
        Counter.builder("slow_sql_count")
               .tag("sql_id", sqlId)
               .register()
               .inc();
    }
}

4.3 可視化與告警

結合Grafana等工具創(chuàng)建SQL性能儀表盤:

  1. 按SQL ID統(tǒng)計平均耗時
  2. 慢查詢發(fā)生頻率
  3. SQL執(zhí)行次數(shù)統(tǒng)計

設置告警規(guī)則:

  • 單個SQL平均耗時連續(xù)5分鐘 > 500ms
  • 慢查詢頻率每分鐘 > 10次

五、經(jīng)驗總結與避坑指南

在八年開發(fā)實踐中,我總結了以下重要經(jīng)驗:

謹慎處理參數(shù)日志

if (paramStr.length() > 1000) {
    paramStr = paramStr.substring(0, 1000) + "...[truncated]";
}
  • 必須進行敏感信息脫敏
  • 大對象參數(shù)截斷處理

區(qū)分環(huán)境配置

  • 開發(fā)環(huán)境:記錄詳細SQL和參數(shù)
  • 生產(chǎn)環(huán)境:僅記錄慢查詢,關閉參數(shù)打印

線程安全考量

  • 攔截器是單例的,所有狀態(tài)變量必須線程安全
  • 使用ThreadLocal存儲耗時上下文

避免過度監(jiān)控

  • 核心業(yè)務SQL重點監(jiān)控
  • 低頻管理后臺SQL可降低監(jiān)控強度

與其他監(jiān)控工具協(xié)同

  • 整合鏈路追蹤(如SkyWalking)的TraceID
  • 與APM工具協(xié)同工作

六、結論

記錄MyBatis SQL執(zhí)行耗時不是目的,而是優(yōu)化系統(tǒng)性能的手段。通過本文介紹的攔截器方案,我們可以:

  1. 精準定位性能瓶頸
  2. 建立SQL性能基線
  3. 主動發(fā)現(xiàn)慢查詢問題
  4. 量化SQL優(yōu)化效果

技術不是目的,而是解決問題的工具。八年的經(jīng)驗告訴我:最好的優(yōu)化發(fā)生在設計階段,最有效的監(jiān)控是預防性監(jiān)控。

以上就是MyBatis SQL耗時監(jiān)控的最佳實踐的詳細內(nèi)容,更多關于MyBatis SQL耗時監(jiān)控的資料請關注腳本之家其它相關文章!

相關文章

  • JUnit中獲取測試類及方法的名稱實現(xiàn)方法

    JUnit中獲取測試類及方法的名稱實現(xiàn)方法

    這篇文章主要介紹了JUnit中獲取測試類及方法的名稱實現(xiàn)方法,本文使用了JUnit中提供的TestName實現(xiàn),不過還有一些編程細節(jié)需要注意,需要的朋友可以參考下
    2015-06-06
  • Java關鍵字volatile和synchronized作用和區(qū)別

    Java關鍵字volatile和synchronized作用和區(qū)別

    這篇文章主要為大家詳細介紹了Java關鍵字volatile和synchronized的作用和區(qū)別,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Java中的Lombok使用及工作原理詳解

    Java中的Lombok使用及工作原理詳解

    這篇文章主要介紹了Java中的Lombok使用及工作原理詳解,Lombok是一個Java庫,能自動插入編輯器并構建工具,簡化Java開發(fā),通過添加注解的方式,不需要為類編寫getter或eques方法,同時可以自動化日志變量,需要的朋友可以參考下
    2023-10-10
  • java?Stream流常見操作方法(反射,類加載器,類加載,反射)

    java?Stream流常見操作方法(反射,類加載器,類加載,反射)

    這篇文章主要介紹了java?Stream流常見操作方法(反射,類加載器,類加載,反射),文章圍繞主題展開詳細的內(nèi)容介紹,展開詳細的內(nèi)容介紹,具有一定參考價值,感興趣的小伙伴可以參考一下
    2022-06-06
  • 使用Java后臺實現(xiàn)彈出框頁面的代碼案例

    使用Java后臺實現(xiàn)彈出框頁面的代碼案例

    在現(xiàn)代Web應用中,彈出框(Modal)是一個常見且重要的UI組件,通過彈出框,我們可以實現(xiàn)用戶交互、表單提交、信息提示等功能,本篇博客將詳細介紹如何使用Java后臺實現(xiàn)彈出框頁面,并展示具體的代碼案例和運行效果,需要的朋友可以參考下
    2024-08-08
  • 10種簡單的Java性能優(yōu)化

    10種簡單的Java性能優(yōu)化

    你是否正打算優(yōu)化hashCode()方法?是否想要繞開正則表達式?Lukas Eder介紹了很多簡單方便的性能優(yōu)化小貼士以及擴展程序性能的技巧
    2017-11-11
  • 一文系統(tǒng)梳理Java中字符串比較的高頻誤區(qū)

    一文系統(tǒng)梳理Java中字符串比較的高頻誤區(qū)

    本文圍繞?Java?字符串比較中的高頻誤區(qū)展開,系統(tǒng)梳理?==、equals()、字符串常量池、new?String()、編譯期與運行期拼接、final?常量折疊以及?intern()?的核心規(guī)則,希望幫助?Java?初學者真正理解?String?比較背后的引用關系與常見踩坑點
    2026-05-05
  • SpringBoot中定位切點的兩種常用方法

    SpringBoot中定位切點的兩種常用方法

    有時候,我們使用AOP來進行放的增強,編寫切面類的時候,需要定位在哪個方法上試用該切面進行增強,本片文章主要講解兩種在SpringBoot中定位切點的方法,感興趣的可以了解一下
    2021-06-06
  • Mac安裝Maven的幾種方法小結

    Mac安裝Maven的幾種方法小結

    本文主要介紹了Mac安裝Maven的幾種方法小結,主要包括通過Homebrew安裝Maven,通過SDKMAN安裝Maven和通過官方網(wǎng)站下載安裝包安裝Maven,感興趣的可以了解一下
    2024-01-01
  • SpringBoot中@ComponentScan注解過濾排除不加載某個類的3種方法

    SpringBoot中@ComponentScan注解過濾排除不加載某個類的3種方法

    這篇文章主要給大家介紹了關于SpringBoot中@ComponentScan注解過濾排除不加載某個類的3種方法,文中通過實例代碼介紹的非常詳細,對大家學習或者使用SpringBoot具有一定的參考學習價值,需要的朋友可以參考下
    2023-07-07

最新評論

定陶县| 肇州县| 鄱阳县| 三门峡市| 循化| 九龙县| 罗平县| 犍为县| 手机| 犍为县| 北安市| 博野县| 龙门县| 宜黄县| 陆良县| 东辽县| 宜城市| 朔州市| 杨浦区| 林周县| 湾仔区| 珠海市| 景谷| 漳州市| 阿克| 广昌县| 沙河市| 宁蒗| 社会| 兴隆县| 上虞市| 东阿县| 南江县| 岳阳县| 麻栗坡县| 渑池县| 霞浦县| 九江县| 晋州市| 和田市| 南江县|