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

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

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

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

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

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

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

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

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

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

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

3.1 實(shí)現(xiàn)原理

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

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

3.2 完整代碼實(shí)現(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");
    
    // 慢查詢(xún)閾值(毫秒)
    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í)行目標(biāo)方法
        } finally {
            long cost = System.currentTimeMillis() - start;
            logSqlPerformance(sqlId, sql, parameterObject, cost);
        }
    }
    
    private void logSqlPerformance(String sqlId, String sql, 
                                   Object params, long cost) {
        // 基礎(chǔ)日志
        String logMsg = String.format("SQL執(zhí)行耗時(shí): %dms | ID: %s | SQL: %s",
                                     cost, sqlId, sql);
        
        // 參數(shù)日志(敏感數(shù)據(jù)需脫敏)
        if (showParameters && params != null) {
            String paramStr = params.toString();
            // 簡(jiǎn)單脫敏處理(實(shí)際項(xiàng)目應(yīng)使用專(zhuān)業(yè)脫敏工具)
            paramStr = paramStr.replaceAll("(\"password\":\")([^\"]+)(\")", 
                                          "$1****$3");
            logMsg += " | 參數(shù): " + paramStr;
        }
        
        // 分級(jí)日志輸出
        if (cost > slowQueryThreshold) {
            logger.warn("[慢查詢(xún)告警] {}", 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 關(guān)鍵代碼解析

  1. @Intercepts注解:定義攔截的類(lèi)和方法簽名
  2. BoundSql獲取:通過(guò)MappedStatement獲取實(shí)際執(zhí)行的SQL
  3. 參數(shù)脫敏處理:防止敏感數(shù)據(jù)(如密碼)泄露到日志
  4. 分級(jí)日志輸出:根據(jù)耗時(shí)長(zhǎng)短使用不同日志級(jí)別
  5. 可配置化:通過(guò)setProperties方法實(shí)現(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以上視為慢查詢(xún)
        properties.setProperty("showParameters", "true");
        interceptor.setProperties(properties);
        return interceptor;
    }
}

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

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

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

private final ExecutorService logExecutor = 
    Executors.newSingleThreadExecutor();

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

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

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

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

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

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

4.3 可視化與告警

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

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

設(shè)置告警規(guī)則:

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

五、經(jīng)驗(yàn)總結(jié)與避坑指南

在八年開(kāi)發(fā)實(shí)踐中,我總結(jié)了以下重要經(jīng)驗(yàn):

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

if (paramStr.length() > 1000) {
    paramStr = paramStr.substring(0, 1000) + "...[truncated]";
}
  • 必須進(jìn)行敏感信息脫敏
  • 大對(duì)象參數(shù)截?cái)嗵幚?/li>

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

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

線(xiàn)程安全考量

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

避免過(guò)度監(jiān)控

  • 核心業(yè)務(wù)SQL重點(diǎn)監(jiān)控
  • 低頻管理后臺(tái)SQL可降低監(jiān)控強(qiáng)度

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

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

六、結(jié)論

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

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

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

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

相關(guān)文章

  • JUnit中獲取測(cè)試類(lèi)及方法的名稱(chēng)實(shí)現(xiàn)方法

    JUnit中獲取測(cè)試類(lèi)及方法的名稱(chēng)實(shí)現(xiàn)方法

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

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

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

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

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

    java?Stream流常見(jiàn)操作方法(反射,類(lèi)加載器,類(lèi)加載,反射)

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

    使用Java后臺(tái)實(shí)現(xiàn)彈出框頁(yè)面的代碼案例

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

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

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

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

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

    SpringBoot中定位切點(diǎn)的兩種常用方法

    有時(shí)候,我們使用AOP來(lái)進(jìn)行放的增強(qiáng),編寫(xiě)切面類(lèi)的時(shí)候,需要定位在哪個(gè)方法上試用該切面進(jìn)行增強(qiáng),本片文章主要講解兩種在SpringBoot中定位切點(diǎn)的方法,感興趣的可以了解一下
    2021-06-06
  • Mac安裝Maven的幾種方法小結(jié)

    Mac安裝Maven的幾種方法小結(jié)

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

    SpringBoot中@ComponentScan注解過(guò)濾排除不加載某個(gè)類(lèi)的3種方法

    這篇文章主要給大家介紹了關(guān)于SpringBoot中@ComponentScan注解過(guò)濾排除不加載某個(gè)類(lèi)的3種方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-07-07

最新評(píng)論

礼泉县| 邮箱| 颍上县| 长乐市| 崇信县| 横山县| 即墨市| 宝坻区| 永泰县| 新巴尔虎左旗| 拉萨市| 辽宁省| 静安区| 交口县| 河西区| 德令哈市| 巴彦县| 台南市| 仁寿县| 兴国县| 清流县| 香港 | 绿春县| 山西省| 南投县| 高清| 南岸区| 九龙城区| 麦盖提县| 恩施市| 怀安县| 西平县| 景东| 乐平市| 灵石县| 铅山县| 凭祥市| 新平| 句容市| 湖北省| 丹寨县|